Ustaw motyw dla fragmentu

117

Próbuję ustawić motyw dla fragmentu.

Ustawienie motywu w manifeście nie działa:

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

Patrząc na poprzednie blogi, wydaje się, że muszę użyć ContextThemeWrapper. Czy ktoś może skierować mnie do zakodowanego przykładu? Nie mogę nic znaleźć.

user1232691
źródło

Odpowiedzi:

187

Ustawienie motywu w manifeście jest zwykle używane do działania.

Jeśli chcesz ustawić motyw dla fragmentu, dodaj następny kod w onCreateView () fragmentu:

@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);
}
David
źródło
30
to nie działa dla mnie. Fragment nadal ma ten sam motyw, który jest określony w pliku manifestu.
Giorgi
1
W manifeście określasz motyw dla działania, a nie fragment. czy używasz fragment czy fragmentActivity?
David
1
Pracował dla mnie. Z klasycznym fragmentem w ramach działania.
David
8
@David „Ustawienie motywu w manifeście służy do działania”. To nie jest do końca prawdą. Jeśli używasz FragmentTransaction, aby dodać fragment w czasie wykonywania, ten motyw jest również stosowany do fragmentów.
sebster,
4
Wydaje się, że to nie działa w przypadku SherlockFragmentActivity z biblioteki ActionBarSherlock.
toobsco42
18

Fragment bierze swój motyw z jego Aktywności. Każdemu fragmentowi przypisywany jest temat Działania, w którym występuje.

Motyw jest stosowany w metodzie Fragment.onCreateView, w której kod tworzy widoki, które w rzeczywistości są obiektami, w których jest używany motyw.

W Fragment.onCreateView dostajesz parametr LayoutInflater, który rozszerza widoki i przechowuje kontekst używany dla motywu, w rzeczywistości jest to Activity. Więc Twoje zawyżone widoki używają motywu Aktywności.

Aby nadpisać motyw, możesz wywołać LayoutInflater.cloneInContext , który wspomina w Dokumentach, że może być używany do zmiany motywu. Możesz tutaj użyć ContextThemeWrapper. Następnie użyj sklonowanego inflatera, aby utworzyć widoki fragmentów.

Pointer Null
źródło
Jak podaje dokumentacja Google: „... Zwraca nowy obiekt LayoutInflater marki powiązany z danym kontekstem…” - developer.android.com/reference/android/view/ ...
Chris,
bardzo pomocny dodatek do rozwiązania kodu Davids. Dzięki!
muetzenflo
13

Do zastosowania jednego stylu właśnie użyłem

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

w onCreateView()fragmencie przed nadmuchaniem widoku root fragmentu i u mnie działa.

Maskować
źródło
1
min api 23 dlagetContext()
vigilancer
@vigilancer Sprawdź tę odpowiedź, aby rozwiązać problem z minimalnym interfejsem API: stackoverflow.com/questions/36736244/…
rm8x
1
Świetne rozwiązanie. Dodałem kod w onAttach (Context), który również stosuje motyw na wszystkich fragmentach potomnych
crysxd
Może to mieć nieoczekiwane konsekwencje, ponieważ modyfikuje motyw kontekstu (w większości przypadków aktywności). Przyszłe nadmuchanie (na przykład po rotacji) działania spowoduje użycie nowego tematu wszędzie
Irhala
12

Próbowałem również wyświetlić okno dialogowe fragmentów z innym motywem niż jego aktywność i zastosowałem to rozwiązanie . Podobnie jak niektóre osoby wspomniane w komentarzach, nie działało, a okno dialogowe wyświetlało się z tematem określonym w manifeście. Problemem okazało się, że budował dialog używając AlertDialog.Builderw onCreateDialogsposób i tak nie robił stosowanie onCreateViewmetody jak pokazano w odpowiedzi, że powiązany. A kiedy tworzyłem instancję AlertDialog.BuilderI przechodziłem w kontekście używając, getActivity()kiedy powinienem był używać instancji ConstextThemeWrapperzamiast tego.

Oto kod mojego 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;
}

Pierwotnie AlertDialog.Builderutworzyłem instancję w następujący sposób:

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

które zmieniłem na:

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

Po tej zmianie pojawiło się okno dialogowe fragmentu z poprawnym motywem. Więc jeśli ktoś ma podobny problem i korzysta z AlertDialog.Buildertego, sprawdź kontekst przekazywany do buildera. Mam nadzieję że to pomoże! :)

BruceHill
źródło
9

Upewnij się, że android:minSdkVersion="11"ustawiłeś w swoim manifeście. To może być powód, dla którego przykład Davida nie zadziałał dla ciebie.

Ustaw również android:theme="@android:style/Theme.Holo.Light"atrybut dla <application>tagu, a NIE dla <activity>tagu.

Innym możliwym problemem może być sposób uzyskiwania kontekstu podczas korzystania z pliku ContextThemeWrapper(). Jeśli używasz czegoś takiego, getActivity().getApplicationContext()po prostu zamień to na getActivity().

Zwykle Theme.Holo powinno dotyczyć fragmentów połączonych z MainActivity.

Zwróć uwagę, że używasz ContextThemeWrapper, gdy chcesz zastosować inny motyw dla swojego fragmentu. Może pomóc, jeśli podasz fragment kodu z MainActivity, w którym dodasz swoje fragmenty.


Kilka przydatnych linków:

Niestandardowy ListView w fragmencie nie jest zgodny z motywem nadrzędnym

sebster
źródło
8

Wypróbowałem rozwiązanie, które David zasugerował, że działa, ale nie we wszystkich scenariuszach:
1. dla pierwszego fragmentu, który został dodany do stosu, ma temat działania, a nie ten, który zdefiniowano w onCrateView, ale na drugim fragmencie, który i dodaj do stosu popraw, czy zostały nałożone na fragment.

2. Na drugim fragmencie, który wyświetlił się poprawnie, wykonałem następujące czynności: wymusiłem zamknięcie aplikacji poprzez wyczyszczenie pamięci, ponowne otwarcie aplikacji i po odtworzeniu działania z fragmentem Fragment zmienił ich błędnie Activity, a nie to samo, które zostało ustawione w onCrateView fragmentu.

Aby rozwiązać ten problem, dokonałem niewielkiej zmiany i zastąpiłem argument kontenera z inflater.inflate wartością null.

Nie wiem, w jaki sposób inflater wykorzystuje w niektórych scenariuszach kontekst z widoku kontenera.

Uwaga - używam 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
źródło
Dzięki, twoje rozwiązanie mnie ratuje. Motyw My DialogFragment różnił się nieco od pozostałych fragmentów. To sprawiło, że mój EditText stał się dziwny, ponieważ użyłem dostarczonego inflatora. Znalazłem pomoc dotyczącą ContextThemeWrapper, ale nie pokazali, jak to się robi. Twoje rozwiązanie działa dla mnie. Wielkie dzięki.
Phuong Dao
4

Wiem, że jest trochę za późno, ale może pomóc komuś innemu, ponieważ pomogło mi.Możesz także spróbować dodać wiersz kodu poniżej wewnątrz funkcji onCreatView fragmentu

    inflater.context.setTheme(R.style.yourCustomTheme)
cbizzy
źródło
2

Utwórz klasę Java, a następnie użyj układu, którego motyw chcesz zmienić w metodzie onCreate, a następnie wspomnij o tym w pliku manifestu jak zwykle

Devam03
źródło
1

Jeśli chcesz tylko zastosować styl do konkretnego fragmentu, po prostu dodaj te linie przed wywołaniem onCreateView()lub onAttach()fragmentem,

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

a jeśli chcesz ustawić przezroczysty pasek stanu, ustaw wartość false dla fitsSystemWindowswłaściwości układu głównego,

android:fitsSystemWindows="false"
Siddharth Sheth
źródło
1

Mam to do pracy, ustawiając motyw w kontekście fragmentu przed wywołaniem inflatora.

UWAGA: To jest przykład dla platformy Xamarin.Android w połączeniu z MvvmCross. Nie jestem w 100% pewien, czy to zadziała również dla programistów Java. Ale możesz spróbować :)

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;
}

Kod metody rozszerzenia 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);
}

Mam nadzieję, że to pomoże niektórym ludziom, na zdrowie!

Jop Middelkamp
źródło
1

Rozwiązałem problem używając android:theme = "@style/myTheme"w pliku układu fragmentu. Na przykład zmienia to styl pliku LinearLayouti jego zawartość, ale nie zmienia niczego poza LinearLayout. Tak więc, aby ozdobić cały fragment dowolnym stylem, zastosuj motyw do jego zewnętrznego układu nadrzędnego.

Uwaga: na wszelki wypadek, jeśli jeszcze nie znalazłeś rozwiązania, możesz spróbować.

           <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>
Niraj Niroula
źródło
-1

możesz spróbować tego dla Lollipopa w onAttach

final Window window = activity.getWindow (); window.setStatusBarColor (myStatusBarColor)

i ustaw go z powrotem na domyślny w ondettach

Amjed Baig
źródło