2010-03-20 4 views
121

Я создал функцию PreferenceActivity, которая позволяет пользователю выбирать тему, которую он хочет применить ко всему приложению.Как изменить текущую тему во время работы в Android

Когда пользователь выбирает тему, этот код будет выполнен:

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

Но, несмотря на то, что я проверил с помощью отладчика, что код выполняется, я не вижу каких-либо изменений в пользовательский интерфейс.

Темы определены в res/values/styles.xml и Eclipse не обнаруживает ошибок.

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

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

Любое представление о том, что может произойти и как его исправить? Должен ли я позвонить setTheme в любой специальной точке кода? Мое приложение состоит из нескольких видов деятельности, если это помогает.

+0

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

ответ

67

Я хотел бы также увидеть метод, где вы задали один раз для всех своих действий. Но насколько я знаю, вы должны установить в каждом мероприятии, прежде чем показывать какие-либо мнения.

Для контрольной проверки это:

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

Edit (скопировано с этого форума):

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

Это работает, но изменения не применяются до тех пор, пока вы не перейдете от одной операции к другой ... –

+4

Это потому, что согласно документации (которую вы должны действительно рассмотрите возможность чтения, много классных вещей в нем). «Это должно быть вызвано до того, как любые представления будут созданы в Контексте» (в http://developer.android.com/reference/android/view/ContextThemeWrapper.html#setTheme%28int% 29) – njzk2

+12

Просьба предоставить автономный ответ, чтобы, если эта ссылка погибнет, у людей все равно будет доступ к вашему ответу –

1

Вы можете закончить Acivity и воссоздать его впоследствии таким образом ваша деятельность будет создана и все представления будут созданы с новой темой.

9

Мы должны установить тему перед вызовом 'super.onCreate()' и 'setContentView()' метод.

Отметьте это link для применения новой темы для всего приложения во время выполнения.

+0

Видимо, это означает «перед вызовом * super.onCreate() * и setContentView()». Кстати, я ранее отрицал этот ответ, потому что это казалось неправильным; теперь я понимаю, что имел в виду, и я вижу в нем ценность (хотя она не была четко выражена). Тем не менее, я не могу изменить свой голос, если ответ не отредактирован. Если это произойдет, пожалуйста, сообщите мне, чтобы я мог изменить свой голос. – LarsH

+0

@LarsH ха-ха-ха Нет проблем дорого. –

+1

Я отредактировал его и удалил downvote. – LarsH

15

У меня такая же проблема, но я нашел решение.

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

Это, когда вы хотите, чтобы пользователь выбирал тему во время выполнения. Не нужно указывать в манифесте – Unknown

+0

Это работает. Я связал его с конфигурацией sharedpreferences, поэтому изменение темы является постоянным. – pandalion98

+2

- вызов 'super.onCreate()' после 'setTheme()' также критически, чтобы сделать эту работу? У меня создалось впечатление, что нужно просто вызвать 'setTheme()' перед вызовом 'setContentView()' –

43

Если вы хотите сменить тему уже существующей деятельности, называют recreate() после setTheme().

Примечание: не вызывайте воссоздание, если вы меняете тему в onCreate(), чтобы избежать бесконечного цикла.

+1

. Этот ответ был бы более полезен, если бы он привел некоторые доказательства или аргументы, чтобы объяснить, почему это необходимо, чтобы вызвать 'recreate() ', хотя документация (http://developer.android.com/reference/android/content/Context.html#setTheme(int)) просто говорит' setTheme() 'перед тем, как будут созданы какие-либо представления. Вы хотите вызвать recreate() только в том случае, если представления уже созданы? – LarsH

+3

В частности, если вы вызываете 'setTheme()' в свой 'onCreate()', перед вызовом либо super.onCreate(), либо setContentView(), (1) вам нужно будет проверить, чтобы защитить от бесконечного цикла где вы продолжаете воссоздавать; и (2) что вы получили в любом случае? – LarsH

7

У меня была аналогичная проблема, и я решил таким образом ..

@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 

} 

этот код для воссоздают активности сохранения Bundle и изменения темы. Вы должны написать свой собственный onSaveInstanceState (Bundle outState); Из API-11 вы можете использовать метод заново() вместо

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() (как уже упоминалось TPReal) будет только перезапустить текущую деятельность, но предыдущие мероприятия будут по-прежнему находиться в задней стеке и тема не будет применяться к их.

Таким образом, еще одно решение этой проблемы, чтобы полностью воссоздать стек задачи, например:

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

EDIT:

Просто поместите код, приведенный выше, после выполнения изменения темы на пользовательском интерфейсе где-нибудь еще. Все ваши действия должны иметь метод setTheme(), вызываемый до onCreate(), возможно, в какой-либо родительской деятельности. Это также нормальный подход к сохранению темы, выбранной в SharedPreferences, прочитайте ее, а затем установите с помощью метода setTheme().

+0

Куда вы поместите этот код? Если вы поместите его в Activity.onCreate(), это приведет к бесконечному циклу? – LarsH

+0

Просто поместите этот код после выполнения изменения темы в пользовательском интерфейсе или в другом месте. Все ваши действия должны иметь метод 'setTheme()', вызываемый до 'onCreate()', возможно, в некоторых родительских действиях. –

+0

Спасибо за терпение, когда я попытаюсь это понять. Под «родительской активностью» я предполагаю, что вы имеете в виду начатую деятельность (используя 'startActivity()') активность, тема которой я хочу изменить. – LarsH

2

Я знаю, что я опаздываю, но я хотел бы опубликовать решение здесь: Проверьте полный исходный код here. Это код, который я использовал при смене темы, используя предпочтения ..

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

Вместо

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

использование

setTheme(R.style.BlackTheme); 

Мой код: в OnCreate() метод:

super.onCreate(savedInstanceState); 

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

setContentView(R.layout.activity_some_layout); 

Где-то (например, на нажатие кнопки):

YourActivity.this.recreate(); 

Вы должны воссоздать деятельность, в противном случае - изменение не произойдет

0

вызова SetContentView (Resource.Layout.Main) после setTheme().

0

Это не имело никакого эффекта для меня:

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

Но это сработало:

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

В соответствии с вашим кодом вы устанавливаете тему переменной, которая никогда не используется. Я считаю, что setTheme внутри onCreate должен быть '' 'setTheme (тема);' '' – LoungeKatt

2

Таким образом, работа для меня:

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

    setContentView(R.layout.activity_main); 
} 

Затем вы хотите изменить новую тему :

GApplication.getInstance().setTheme(R.style.LightTheme); 
recreate(); 
Смежные вопросы