Установить тему для фрагмента

117

Пытаюсь задать тему для фрагмента.

Установка темы в манифесте не работает:

android:theme="@android:style/Theme.Holo.Light"

Глядя на предыдущие блоги, кажется, что мне нужно использовать ContextThemeWrapper. Может ли кто-нибудь сослаться на закодированный пример? Я ничего не могу найти.

user1232691
источник

Ответы:

187

Настройка Theme в манифесте обычно используется для Activity.

Если вы хотите установить тему для фрагмента, добавьте следующий код в onCreateView () фрагмента:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

    // create ContextThemeWrapper from the original Activity Context with the custom theme
    final Context contextThemeWrapper = new ContextThemeWrapper(getActivity(), R.style.yourCustomTheme);

    // clone the inflater using the ContextThemeWrapper
    LayoutInflater localInflater = inflater.cloneInContext(contextThemeWrapper);

    // inflate the layout using the cloned inflater, not default inflater
    return localInflater.inflate(R.layout.yourLayout, container, false);
}
Дэвид
источник
30
это не работает для меня. Фрагмент по-прежнему имеет ту же тему, которая указана в файле манифеста.
Giorgi
1
В манифесте вы указываете тему действия, а не фрагмент. вы используете фрагмент или фрагментактивность?
Дэвид
1
Работал у меня. С классическим фрагментом в Activity.
Дэвид
8
@David «Настройка темы в манифесте используется для Activity». Это не совсем правда. Если вы используете FragmentTransaction для добавления фрагмента во время выполнения, эта тема также применяется к фрагментам.
sebster
4
Кажется, это не работает для SherlockFragmentActivity из библиотеки ActionBarSherlock.
toobsco42 05
18

Фрагмент берет свою тему из Activity. Каждому фрагменту назначается тема Деятельности, в которой он существует.

Тема применяется в методе Fragment.onCreateView, где ваш код создает представления, которые на самом деле являются объектами, в которых используется тема.

В Fragment.onCreateView вы получаете параметр LayoutInflater, который расширяет представления и содержит контекст, используемый для темы, на самом деле это Activity. Итак, ваши раздутые представления используют тему Activity.

Чтобы переопределить тему, вы можете вызвать LayoutInflater.cloneInContext , который упоминает в Документах, что его можно использовать для изменения темы. Вы можете использовать здесь ContextThemeWrapper. Затем используйте клонированный надувной элемент для создания представлений фрагментов.

Нулевой указатель
источник
Как говорится в документации Google: «... Возвращает бренд, шлепающий новый объект LayoutInflater, связанный с данным контекстом ...» - developer.android.com/reference/android/view/…
Крис,
очень полезное дополнение к решению кода Дэвидса. Спасибо!
muetzenflo
13

Для применения единого стиля я использовал только

getContext().getTheme().applyStyle(styleId, true);

в onCreateView()фрагменте, прежде чем раздувать корневой вид фрагмента, и это работает для меня.

Tomask
источник
1
min api 23 forgetContext()
vigilancer
@vigilancer Проверьте этот ответ, чтобы исправить проблему с min api: stackoverflow.com/questions/36736244/…
rm8x
1
Отличное решение. Я добавил код в onAttach (Context), который также применяет тему ко всем дочерним фрагментам
crysxd
Это может иметь неожиданные последствия, поскольку изменяет тему контекста (в большинстве случаев - действия). Будущее расширение (например, после ротации) Activity будет использовать новую тему везде
Irhala
12

Я также пытался отобразить диалоговое окно моего фрагмента с темой, отличной от его активности, и следовал этому решению . Как и некоторые люди, упомянутые в комментариях, я не заставлял его работать, и диалог продолжал отображаться с темой, указанной в манифесте. Проблема оказалась в том , что я строила диалог , используя AlertDialog.Builderв onCreateDialogспособе и поэтому не делает использование onCreateViewметода , как показано в ответ , что я связан с. И когда я создавал экземпляр, AlertDialog.Builderя передавал в контексте using, getActivity()когда я должен был использовать ConstextThemeWrapperвместо этого экземпляр .

Вот код моего onCreateDialog:

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    // Create ContextThemeWrapper from the original Activity Context
    ContextThemeWrapper contextThemeWrapper = new ContextThemeWrapper(getActivity(), android.R.style.Theme_DeviceDefault_Light_Dialog);
    LayoutInflater inflater =   getActivity().getLayoutInflater().cloneInContext(contextThemeWrapper);
    // Now take note of the parameter passed into AlertDialog.Builder constructor
    AlertDialog.Builder builder = new AlertDialog.Builder(contextThemeWrapper);
    View view = inflater.inflate(R.layout.set_server_dialog, null);
    mEditText = (EditText) view.findViewById(R.id.txt_server);
    mEditText.requestFocus();  // Show soft keyboard automatically
    mEditText.setOnEditorActionListener(this);
    builder.setView(view);
    builder.setTitle(R.string.server_dialog);
    builder.setPositiveButton(android.R.string.ok, this);
    Dialog dialog = builder.create();
    dialog.setCanceledOnTouchOutside(false);
    return dialog;
}

Изначально у меня был AlertDialog.Builderэкземпляр, созданный следующим образом:

AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

который я изменил на:

AlertDialog.Builder builder = new AlertDialog.Builder(contextThemeWrapper);

После этого изменения диалог фрагмента был показан с правильной темой. Итак, если у кого-то еще есть аналогичная проблема и он использует, AlertDialog.Builderто проверьте контекст, передаваемый строителю. Надеюсь это поможет! :)

BruceHill
источник
9

Убедитесь, что вы android:minSdkVersion="11"установили в своем манифесте. Это могло быть причиной того, что пример Дэвида вам не подошел.

Кроме того, установите android:theme="@android:style/Theme.Holo.Light"атрибут для <application>тега, а НЕ для <activity>тега.

Другая возможная проблема может заключаться в способе получения контекста при использовании файла ContextThemeWrapper(). Если вы используете что-то вроде, getActivity().getApplicationContext()просто замените его на getActivity().

Обычно Theme.Holo должен применяться к фрагментам, связанным с MainActivity.

Обратите внимание, что вы используете ContextThemeWrapper, когда хотите применить другую тему для своего фрагмента. Может помочь, если вы предоставите фрагмент кода из MainActivity, куда вы добавляете свои фрагменты.


Полезные ссылки:

Пользовательский ListView во фрагменте не соответствует родительской теме

sebster
источник
8

Я попробовал решение, которое, по мнению Дэвида, работает, но не во всех сценариях:
1. для первого фрагмента, добавленного в стек, есть тема действия, а не та, которая определена в onCrateView, а для второго фрагмента, который я добавить в стек правильно их было применено к фрагменту.

2. Во втором фрагменте, который они отображали правильно, я сделал следующее: я принудительно закрыл приложение, очистив память, повторно открыв приложение, и когда действие было воссоздано с фрагментом, фрагмент изменил их неправильно. Activity, а не то же самое, что было установлено в onCrateView фрагмента.

Чтобы решить эту проблему, я сделал небольшое изменение и заменил аргумент контейнера из inflater.inflate нулевым.

Я не знаю, как надувной модуль использует в некоторых сценариях контекст из представления контейнера.

Обратите внимание: я использую android.support.v4.app.Fragment и android.support.v7.app.AppCompatActivity.

@Override 
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle   savedInstanceState) {

// create ContextThemeWrapper from the original Activity Context with the custom theme 
final Context contextThemeWrapper = new ContextThemeWrapper(getActivity(), R.style.yourCustomTheme);

// clone the inflater using the ContextThemeWrapper 
LayoutInflater localInflater = inflater.cloneInContext(contextThemeWrapper);

// inflate the layout using the cloned inflater, not default inflater 
return localInflater.inflate(R.layout.yourLayout, null, false);
} 
AVI
источник
Спасибо, ваше решение меня спасает. Тема моего DialogFragment чем-то отличалась от других фрагментов. Это сделало мой EditText странным, потому что я использовал предоставленный inflater. Я нашел некоторые подсказки относительно ContextThemeWrapper, но они не показали, как это делается. Ваше решение работает для меня. Большое спасибо.
Phuong Dao
4

Я знаю, что это немного поздно, но это может помочь кому-то другому, потому что это помогло мне. Вы также можете попробовать добавить строку кода ниже внутри функции onCreatView фрагмента

    inflater.context.setTheme(R.style.yourCustomTheme)
cbizzy
источник
2

Создайте класс java, а затем используйте макет, тему которого вы хотите изменить, в методе onCreate. Затем укажите его в манифесте как обычно.

Devam03
источник
1

Если вы просто хотите применить стиль для определенного фрагмента, просто добавьте эти строки перед вызовом onCreateView()или onAttach()фрагмента,

getActivity().getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
getActivity().getWindow().setStatusBarColor(Color.TRANSPARENT);

и если вы хотите установить прозрачную строку состояния, установите false для fitsSystemWindowsсвойства вашего корневого макета,

android:fitsSystemWindows="false"
Сиддхарт Шет
источник
1

Я заставил его работать, установив тему в контексте фрагмента перед вызовом инфлятора.

ПРИМЕЧАНИЕ. Это пример для Xamarin.Android в сочетании с MvvmCross. Я не уверен на 100%, что это сработает и для Java-программистов. Но можно попробовать :)

public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
    Context.SetTheme(Theme);

    base.OnCreateView(inflater, container, savedInstanceState);

    var view = this.BindingInflate(FragmentLayoutId, container, false);

    // Doing some other stuff

    return view;
}

Код метода расширения SetTheme

public static void SetTheme(this Context context, AppThemeStyle themeStyle)
{
    var themeStyleResId = themeStyle == AppThemeStyle.Dark ? Resource.Style.AppTheme : Resource.Style.AppTheme_Light;

    context.SetTheme(themeStyleResId);
}

Надеюсь, это поможет некоторым людям, ура!

Джоп Мидделкамп
источник
1

Решил проблему, используя android:theme = "@style/myTheme"в файле макета фрагмента. Например, это изменяет стиль LinearLayoutи его содержимое, но ничего кроме LinearLayout. Итак, чтобы оформить весь фрагмент каким-либо стилем, примените тему к его самому внешнему родительскому макету.

Примечание: на всякий случай, если вы еще не нашли решения, вы можете попробовать.

           <LinearLayout 
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:theme = "@style/myTheme" >

            <TextView
                android:id="@+id/tc_buttom_text1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Time elapsed"/>

            <TextView
                android:id="@+id/tc_buttom_text2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="10dp"
                android:text="00:00:00 00"/>
        </LinearLayout>
Нирадж Нирула
источник
-1

вы можете попробовать это для леденца на палочке в onAttach

последнее окно window = activity.getWindow (); window.setStatusBarColor (myStatusBarColor)

и верните его по умолчанию в ondettach

Амджед Байг
источник