132

I'm trying to set the theme for a fragment.

Setting the theme in the manifest does not work:

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

From looking at previous blogs, it appears as though I have to use a ContextThemeWrapper. Can anyone refer me to a coded example? I can't find anything.

Peter O.
  • 32,158
  • 14
  • 82
  • 96
user1232691
  • 1,323
  • 2
  • 10
  • 5

13 Answers13

218

Setting Theme in manifest is usually used for Activity.

If you want to set Theme for Fragment, add next code in the onGetLayoutInflater() of the Fragment:

override fun onGetLayoutInflater(savedInstanceState: Bundle?): LayoutInflater {
    val inflater = super.onGetLayoutInflater(savedInstanceState)
    val contextThemeWrapper: Context = ContextThemeWrapper(requireContext(), R.style.yourCustomTheme)
    return inflater.cloneInContext(contextThemeWrapper)
}
Rubén Viguera
  • 3,277
  • 1
  • 17
  • 31
David
  • 37,109
  • 32
  • 120
  • 141
  • 48
    this does not work for me. The fragment still has the same theme that is specified in the manifest file. – Giorgi Mar 20 '13 at 14:25
  • 1
    In the manifest you specify the Theme for the activity, not the fragment. are you using fragment or fragmentActivity? – David Mar 20 '13 at 15:59
  • I'm using a ListFragment in a FragmentActivity – Giorgi Mar 20 '13 at 18:12
  • 1
    Worked for me. With classic Fragment within Activity. – David Mar 21 '13 at 10:29
  • 8
    @David "Setting Theme in manifest is used for Activity". This is not entirely true. If you use a FragmentTransaction to add your Fragment at runtime that theme is applied to the Fragments as well. – sebster Mar 24 '13 at 23:04
  • 4
    This does not seem to work for a SherlockFragmentActivity from the ActionBarSherlock library. – Etienne Lawlor Nov 05 '13 at 18:28
  • i had same problem and it worked for me but i have fragment not fragment activity – Milaaaad Nov 25 '15 at 06:11
  • @Giorgi Were you able to get this working? I've a requirement of setting theme in Fragment, which is inside FragmentActivity. – LoveForDroid Apr 05 '19 at 19:07
  • @LoveForDroid This was 6 years ago, I definitely don't remember it :) – Giorgi Apr 05 '19 at 19:14
  • Does anyone know, is this expensive? Should the ContextWrapper be cached? – urSus Jun 01 '19 at 21:34
  • Make sure you have applied the custom attributes in the XML file. Using ContextWrapper instead of Context also works – Priyanthi May 21 '21 at 21:09
26

Fragment takes its theme from its Activity. Each fragment gets assigned the theme of the Activity in which it exists.

The theme is applied in Fragment.onCreateView method, where your code creates views, which are actually objects where theme is used.

In Fragment.onCreateView you get LayoutInflater parameter, which inflates views, and it holds Context used for theme, actually this is the Activity. So your inflated views use Activity's theme.

To override theme, you may call LayoutInflater.cloneInContext, which mentions in Docs that it may be used for changing theme. You may use ContextThemeWrapper here. Then use cloned inflater to create fragment's views.

Ojonugwa Jude Ochalifu
  • 26,627
  • 26
  • 120
  • 132
Pointer Null
  • 39,597
  • 13
  • 90
  • 111
  • As the google docs states it: "...Returns a brand spanking new LayoutInflater object associated with the given Context..." - http://developer.android.com/reference/android/view/LayoutInflater.html#cloneInContext%28android.content.Context%29 – Chris Nov 06 '14 at 11:41
24

For applying a single style I've used just

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

in onCreateView() of the fragment before inflating root view of the fragment and it works for me.

Tomask
  • 2,344
  • 3
  • 27
  • 37
  • 1
    min api 23 for `getContext()` – vigilancer Sep 07 '16 at 21:54
  • @vigilancer Check this answer for a fix to your min api issue: http://stackoverflow.com/questions/36736244/android-unable-to-set-getcontext-in-non-static-method-requires-api-level-23 – rm8x Oct 27 '16 at 15:17
  • 1
    Great solution. I added the code in onAttach(Context) which also applies the theme on all child fragment – crysxd May 04 '17 at 12:24
  • 1
    This can have unexpected consequences as it modifies the context's (activity in most cases) theme. A future inflating (for example, after a rotation) of the Activity will use the new theme everywhere – Irhala Jan 02 '19 at 13:15
12

I was also trying to get my fragment dialog to display with a different theme to its activity, and followed this solution. Like some people mentioned in the comments, I was not getting it to work and the dialog kept showing with the theme specified in the manifest. The problem turned out to be that I was building the dialog using AlertDialog.Builder in the onCreateDialog method and so was not making use of the onCreateView method as shown in the answer that I linked to. And when I was instantiating the AlertDialog.Builder I was passing in the context using getActivity() when I should have been using the instantiated ConstextThemeWrapper instead.

Here is the code for my 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;
}

I originally had the AlertDialog.Builder being instantiated as follows:

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

which I changed to:

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

After this change the fragment dialog was shown with the correct theme. So if anyone else is having a similar problem and is making using of the AlertDialog.Builder then check the context being passed to the builder. Hope this helps! :)

Community
  • 1
  • 1
BruceHill
  • 6,954
  • 8
  • 62
  • 114
11

I solved the problem using android:theme = "@style/myTheme" in the layout file of the fragment. For instance this changes the style of the LinearLayout and it's content but not any thing out side the LinearLayout. So, in order to decor the whole fragment with any style apply the theme to it's outer most parent layout.

Note: Just in case if you haven't found a solution yet, you can give it a try.

           <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
  • 2,376
  • 1
  • 17
  • 36
9

Make sure you have android:minSdkVersion="11" set in your manifest. This could be the cause why David's example didn't work for you.

Also, set the android:theme="@android:style/Theme.Holo.Light" attribute for the <application> tag and NOT the <activity> tag.

Another possible problem might be the way you get your Context when using a ContextThemeWrapper(). If you use something like getActivity().getApplicationContext() just replace it with getActivity() instead.

Normally, the Theme.Holo should apply to Fragments linked to the MainActivity.

Please note that you use a ContextThemeWrapper when you want to apply a different theme for your Fragment. It might help if you provide the piece of code, from your MainActivity, where you add your Fragments.


Some useful links:

Custom ListView in Fragment not adhering to parent theme

Kushal
  • 8,100
  • 9
  • 63
  • 82
sebster
  • 1,322
  • 2
  • 18
  • 29
8

I tried the solution that David suggested it did works but not in all scenarios:
1. for the first fragment that added to the stack has the the theme of the activity and not the one that defined in onCrateView , but on the second fragment that i add to the stack correct them was applied on the fragment.

2. On the second fragment that the them was displayed correctly ,i did the following i forced the App to be closed by clean the memory , re open the App and when the Activity was recreated with the fragment The fragment changed the them wrong them of the Activity and not the same that was set in the onCrateView of the fragment .

To fix the issue i did a small change and replaced the container argument from the inflater.inflate with a null.

i don't know way the inflater uses in some scenarios the context from the container view .

Note - that im using 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
  • 91
  • 1
  • 3
  • Thanks, your solution saves me. My DialogFragment's theme was somehow different from the other fragments. It made my EditText become weird because I used provided inflater. I found some helps regarding ContextThemeWrapper but they haven't showed how it is done. Your solution works for me. Many thanks. – Phuong Dao May 10 '17 at 05:01
7

I know its a bit late but it might help someone else because this helped me.You can also try adding the line of code below inside the onCreatView function of the fragment

    inflater.context.setTheme(R.style.yourCustomTheme)
cbizzy
  • 109
  • 1
  • 5
  • 3
    It worked for me but I'm wondering if the approach can have side effects on other views. In my case the context is the MainActivity, I'm then overriding the MainActivity's theme. Does it make sense? – andrea simeoni Jun 13 '21 at 15:00
2

Create a java class and then use the layout you want to change the theme of in the onCreate method.Then mention it in manifest as normal

Devam03
  • 141
  • 1
  • 7
2

If you just want to apply style for particular fragment then just add this lines before calling onCreateView() or onAttach() of the fragment,

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

and If you want to set transparent status bar then set false to fitsSystemWindows property of your root layout,

android:fitsSystemWindows="false"
Siddharth Sheth
  • 207
  • 2
  • 6
0

I've got it to work by setting the theme on the fragment context before calling the inflator.

NOTE: This is an example for Xamarin.Android in combination with MvvmCross. I'm not 100% sure if this will also work for the Java programmers. But you can try :)

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

The SetTheme extension method code

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

    context.SetTheme(themeStyleResId);
}

I hope this helps some people out, cheers!

0

Use this in fragment which you want different theme:

@Override
    public void onAttach(@NonNull Context context) {
        context.setTheme(R.style.your_them);
        super.onAttach(context);
    }
mostafa3dmax
  • 997
  • 7
  • 18
-1

you can try this for lollipop in onAttach

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

and set it back to default in ondettach

Amjed Baig
  • 410
  • 5
  • 9