2010-03-20 7 views
121

J'ai créé une PreferenceActivity qui permet à l'utilisateur de choisir le thème qu'il souhaite appliquer à l'ensemble de l'application.Comment modifier le thème actuel lors de l'exécution dans Android

Lorsque l'utilisateur sélectionne un thème, ce code est exécuté:

if (...) { 
    getApplication().setTheme(R.style.BlackTheme); 
} else { 
    getApplication().setTheme(R.style.LightTheme); 
} 

Mais, même si je l'ai vérifié avec le débogueur que le code est en cours d'exécution, je ne vois aucun changement dans la interface utilisateur.

Les thèmes sont définis dans res/values/styles.xml et Eclipse n'indique aucune erreur.

<resources> 
    <style name="LightTheme" parent="@android:style/Theme.Light"> 
    </style> 

    <style name="BlackTheme" parent="@android:style/Theme.Black"> 
    </style>  
</resources> 

Une idée sur ce qui pourrait se produire et comment y remédier? Devrais-je appeler setTheme à un point particulier du code? Mon application se compose de plusieurs activités si cela aide.

+0

http://stackoverflow.com/a/32111974/1318946 –

Répondre

67

Je voudrais voir la méthode aussi, où vous définissez une fois pour toutes vos activités. Mais autant que je sache, vous devez mettre dans chaque activité avant de montrer des vues.

Pour la vérification des références ceci:

http://www.anddev.org/applying_a_theme_to_your_application-t817.html

Modifier (copié de ce forum):

protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 

    // Call setTheme before creation of any(!) View. 
    setTheme(android.R.style.Theme_Dark); 

    // ... 
    setContentView(R.layout.main); 
} 
+2

Cela fonctionne, mais les changements ne sont pas appliqués jusqu'à ce que vous ne changiez pas d'une activité à l'autre ... –

+4

c'est parce que, selon la documentation (que vous devriez vraiment envisager de lire, beaucoup de trucs cool dans ce «) cela devrait être appelé avant que les vues sont instanciés dans le contexte » (en http://developer.android.com/reference/android/view/ContextThemeWrapper.html#setTheme%28int% 29) – njzk2

+12

S'il vous plaît fournir une réponse autonome de sorte que si ce lien meurt les gens auront toujours accès à votre réponse –

1

Vous pouvez terminer la acivités et recréer ensuite de cette façon votre activité sera créée encore et tous les points de vue seront créés avec le nouveau thème.

9

Nous devons mettre le thème avant d'appeler la méthode 'super.onCreate()' et 'setContentView()'.

Consultez ce link pour appliquer un nouveau thème à l'application entière lors de l'exécution.

+0

Apparemment, cela signifie "avant d'appeler * super.onCreate() * et setContentView()". BTW j'ai downvoted cette réponse plus tôt parce que cela semblait faux; maintenant je comprends ce que l'on voulait dire, et je vois de la valeur dedans (bien que cela n'ait pas été clairement exprimé). Je ne peux pas changer mon vote, cependant, à moins que la réponse soit éditée. Si cela se produit, veuillez m'en aviser afin que je puisse changer mon vote. – LarsH

+0

@LarsH ha ha ha Pas de problèmes chers. –

+1

Je l'ai édité et enlevé le downvote. – LarsH

15

J'ai eu le même problème mais j'ai trouvé la solution.

public class EditTextSmartPhoneActivity extends Activity implements DialogInterface.OnClickListener 
{ 
    public final static int CREATE_DIALOG = -1; 
    public final static int THEME_HOLO_LIGHT = 0; 
    public final static int THEME_BLACK = 1; 

    int position; 

    /** Called when the activity is first created. */ 
    @Override 
    public void onCreate(Bundle savedInstanceState) 
    { 
     position = getIntent().getIntExtra("position", -1); 

     switch(position) 
     { 
     case CREATE_DIALOG: 
      createDialog(); 
      break; 
     case THEME_HOLO_LIGHT: 
      setTheme(android.R.style.Theme_Holo_Light); 
      break; 
     case THEME_BLACK: 
      setTheme(android.R.style.Theme_Black); 
      break; 
     default: 
     } 

     super.onCreate(savedInstanceState); 
     setContentView(R.layout.main); 

    } 

    private void createDialog() 
    { 
     /** Options for user to select*/ 
     String choose[] = {"Theme_Holo_Light","Theme_Black"}; 

     AlertDialog.Builder b = new AlertDialog.Builder(this); 

     /** Setting a title for the window */ 
     b.setTitle("Choose your Application Theme"); 

     /** Setting items to the alert dialog */ 
     b.setSingleChoiceItems(choose, 0, null); 

     /** Setting a positive button and its listener */ 
     b.setPositiveButton("OK",this); 

     /** Setting a positive button and its listener */ 
     b.setNegativeButton("Cancel", null); 

     /** Creating the alert dialog window using the builder class */ 
     AlertDialog d = b.create(); 

     /** show dialog*/ 
     d.show(); 
    } 

    @Override 
    public void onClick(DialogInterface dialog, int which) { 
     // TODO Auto-generated method stub 
     AlertDialog alert = (AlertDialog)dialog; 
     int position = alert.getListView().getCheckedItemPosition(); 

     finish(); 
     Intent intent = new Intent(this, EditTextSmartPhoneActivity.class); 
     intent.putExtra("position", position); 
     intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 
     intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 
     startActivity(intent); 
    } 
} 
+3

Ceci lorsque vous voulez que l'utilisateur sélectionne un thème lors de l'exécution. Il n'est pas nécessaire de définir le manifeste – Unknown

+0

Cela fonctionne. Je l'ai couplé avec une configuration de préférences partagées afin que le changement de thème soit persistant. – pandalion98

+2

est l'appel à 'super.onCreate()' après 'setTheme()' aussi critique pour faire ce travail? J'étais sous l'impression qu'il fallait juste appeler 'setTheme()' avant d'appeler 'setContentView()' –

43

Si vous voulez changer le thème d'une activité déjà existante, appelez recreate() après setTheme().

Remarque: ne pas appeler recréer si vous changez de thème dans onCreate(), pour éviter une boucle infinie.

+1

Cette réponse serait plus utile si elle citait une preuve ou un raisonnement expliquant pourquoi il serait nécessaire d'appeler 'recréer() ', même si la documentation (http://developer.android.com/reference/android/content/Context.html#setTheme(int)) dit simplement' setTheme() 'avant que les vues ne soient instanciées. Voulez-vous appeler recréer() seulement si les vues ont déjà été instanciées? – LarsH

+3

En particulier, si vous appelez 'setTheme()' dans votre 'onCreate()', avant d'appeler super.onCreate() ou setContentView(), (1) vous devrez vérifier pour vous protéger contre une boucle infinie où vous continuez à recréer; et (2) qu'avez-vous gagné de toute façon? – LarsH

7

J'ai eu un problème similaire et je résolus de cette façon ..

@Override 
public void onCreate(Bundle savedInstanceState) { 

    if (getIntent().hasExtra("bundle") && savedInstanceState==null){ 
     savedInstanceState = getIntent().getExtras().getBundle("bundle"); 
    } 

    //add code for theme 

    switch(theme) 
    { 
    case LIGHT: 
     setTheme(R.style.LightTheme); 
     break; 
    case BLACK: 
     setTheme(R.style.BlackTheme); 
     break; 

    default: 
    } 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 
    //code 

} 

ce code est pour recréent l'activité d'épargne Bundle et changer le thème. Vous devez écrire votre propre onSaveInstanceState (Bundle outState); De API-11 vous pouvez utiliser la Recréer méthode() au lieu

Bundle temp_bundle = new Bundle(); 
onSaveInstanceState(temp_bundle); 
Intent intent = new Intent(this, MainActivity.class); 
intent.putExtra("bundle", temp_bundle); 
startActivity(intent); 
finish(); 
17

recreate() (comme mentionné par TPReal) ne redémarre que l'activité en cours, mais les activités précédentes seront toujours dans la pile et le thème de retour ne sera pas appliqué à leur.

Ainsi, une autre solution pour ce problème est de recréer la pile de tâches complètement, comme ceci:

TaskStackBuilder.create(getActivity()) 
      .addNextIntent(new Intent(getActivity(), MainActivity.class)) 
      .addNextIntent(getActivity().getIntent()) 
      .startActivities(); 

EDIT:

Il suffit de mettre le code ci-dessus après avoir effectué le changement de thème sur l'interface utilisateur ou ailleurs. Toutes vos activités doivent avoir la méthode setTheme() appelée avant onCreate(), probablement dans une activité parent. Il est également une approche normale pour enregistrer le thème choisi SharedPreferences, lire et mettre en utilisant la méthode setTheme().

+0

Où mettriez-vous ce code? Si vous le mettez dans un Activity.onCreate(), cela conduira-t-il à une boucle sans fin? – LarsH

+0

Il suffit de mettre ce code après avoir effectué le changement de thème sur l'interface utilisateur ou ailleurs. Toutes vos activités devraient avoir la méthode 'setTheme()' appelée avant 'onCreate()', probablement dans une activité parent. –

+0

Merci pour votre patience alors que j'essaie de comprendre cela. Par "activité parentale" je suppose que vous voulez dire l'activité qui a commencé (en utilisant 'startActivity()') l'activité dont le thème que je veux changer. – LarsH

2

Je sais que je suis en retard, mais je voudrais signaler ici une solution: Vérifiez le code source complet here. Ce code est i utilisé lors du changement de thème en utilisant les préférences ..

SharedPreferences pref = PreferenceManager 
     .getDefaultSharedPreferences(this); 
String themeName = pref.getString("prefSyncFrequency3", "Theme1"); 
if (themeName.equals("Africa")) { 
    setTheme(R.style.AppTheme); 
} else if (themeName.equals("Colorful Beach")) { 
    //Toast.makeText(this, "set theme", Toast.LENGTH_SHORT).show(); 
    setTheme(R.style.beach); 
} else if (themeName.equals("Abstract")) { 
    //Toast.makeText(this, "set theme", Toast.LENGTH_SHORT).show(); 
    setTheme(R.style.abstract2); 
} else if (themeName.equals("Default")) { 
    setTheme(R.style.defaulttheme); 
} 
2

Au lieu de

getApplication().setTheme(R.style.BlackTheme); 

utilisation

setTheme(R.style.BlackTheme); 

Mon code: la méthode onCreate():

super.onCreate(savedInstanceState); 

if(someExpression) { 
    setTheme(R.style.OneTheme); 
} else { 
    setTheme(R.style.AnotherTheme); 
} 

setContentView(R.layout.activity_some_layout); 

Quelque part (par exemple, sur un bouton clic):

YourActivity.this.recreate(); 

Vous devez recréer l'activité, sinon - le changement ne se produira pas

0

Appel setContentView (Resource.Layout.Main) après setTheme().

0

Cela n'a eu aucun effet pour moi:

public void changeTheme(int newTheme) { 
    setTheme(newTheme); 
    recreate(); 
} 

Mais cela a fonctionné:

int theme = R.style.default; 

protected void onCreate(Bundle savedInstanceState) { 
    setTheme(this.theme); 
    super.onCreate(savedInstanceState); 
} 

public void changeTheme(int newTheme) { 
    this.theme = newTheme; 
    recreate(); 
} 
+0

Selon votre code, vous définissez le thème sur une variable qui n'est jamais utilisée. Je crois que setTheme dans onCreate devrait être '' 'setTheme (theme);' '' – LoungeKatt

2

De cette façon, le travail pour moi:

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    setTheme(GApplication.getInstance().getTheme()); 
    super.onCreate(savedInstanceState); 

    setContentView(R.layout.activity_main); 
} 

Ensuite, vous voulez changer un nouveau thème :

GApplication.getInstance().setTheme(R.style.LightTheme); 
recreate(); 
Questions connexes