ตั้งค่าธีมสำหรับ Fragment


117

ฉันกำลังพยายามตั้งค่าธีมสำหรับส่วนย่อย

การตั้งค่าธีมในรายการไม่ทำงาน:

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

จากการดูบล็อกก่อนหน้านี้ดูเหมือนว่าฉันต้องใช้ ContextThemeWrapper มีใครแนะนำตัวอย่างโค้ดให้ฉันได้ไหม ฉันไม่พบอะไรเลย

คำตอบ:


187

โดยปกติการตั้งค่า Theme ในไฟล์ Manifest จะใช้สำหรับกิจกรรม

หากคุณต้องการตั้งค่า Theme สำหรับ Fragment ให้เพิ่มโค้ดถัดไปใน onCreateView () ของ Fragment:

@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
ในรายการคุณระบุธีมสำหรับกิจกรรมไม่ใช่แฟรกเมนต์ คุณกำลังใช้ Fragment หรือ FragmentActivity?
เดวิด

1
ทำงานให้ฉัน ด้วย Fragment แบบคลาสสิกภายในกิจกรรม
เดวิด

8
@David "การตั้งค่าธีมในไฟล์ Manifest ใช้สำหรับกิจกรรม" นี่ไม่เป็นความจริงทั้งหมด หากคุณใช้ FragmentTransaction เพื่อเพิ่ม Fragment ของคุณขณะรันไทม์ธีมนั้นจะถูกนำไปใช้กับ Fragments ด้วย
sebster

4
ดูเหมือนจะใช้ไม่ได้กับ SherlockFragmentActivity จากไลบรารี ActionBarSherlock
toobsco42

18

Fragment นำธีมมาจากกิจกรรม แต่ละส่วนได้รับการกำหนดธีมของกิจกรรมที่มีอยู่

ธีมถูกนำไปใช้ในเมธอด Fragment.onCreateView โดยที่โค้ดของคุณสร้างมุมมองซึ่งเป็นอ็อบเจ็กต์ที่ใช้ธีม

ใน Fragment.onCreateView คุณจะได้รับพารามิเตอร์ LayoutInflater ซึ่งจะขยายมุมมองและมี Context ที่ใช้สำหรับธีมจริงๆแล้วนี่คือกิจกรรม การดูที่สูงเกินจริงของคุณจึงใช้ธีมของกิจกรรม

ในการลบล้างธีมคุณสามารถเรียกLayoutInflater.cloneInContextซึ่งกล่าวถึงในเอกสารว่าอาจใช้สำหรับการเปลี่ยนธีม คุณสามารถใช้ ContextThemeWrapper ได้ที่นี่ จากนั้นใช้เครื่องเป่าลมแบบโคลนเพื่อสร้างมุมมองของชิ้นส่วน


ตามที่ Google เอกสารระบุไว้: "... ส่งคืนแบรนด์ที่ตบวัตถุ LayoutInflater ใหม่ที่เชื่อมโยงกับบริบทที่กำหนด ... " - developer.android.com/reference/android/view/…
คริส

นอกจากนี้ยังมีประโยชน์มากสำหรับโซลูชันรหัส Davids ขอบคุณ!
muetzenflo

13

สำหรับการใช้สไตล์เดียวฉันใช้เพียง

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

ในonCreateView()ส่วนก่อนที่จะขยายมุมมองรูทของส่วนและมันก็ใช้ได้สำหรับฉัน


1
min api 23 สำหรับgetContext()
vigilancer

@vigilancer ตรวจสอบคำตอบนี้เพื่อแก้ไขปัญหา min api ของคุณ: stackoverflow.com/questions/36736244/…
rm8x

1
ทางออกที่ดี ฉันเพิ่มรหัสใน onAttach (บริบท) ซึ่งใช้ธีมกับส่วนย่อยทั้งหมดด้วย
crysxd

สิ่งนี้อาจส่งผลที่ไม่คาดคิดได้เนื่องจากมันปรับเปลี่ยนธีมของบริบท (กิจกรรมในกรณีส่วนใหญ่) การขยายตัวในอนาคต (เช่นหลังจากการหมุนเวียน) ของกิจกรรมจะใช้ธีมใหม่ทุกที่
Irhala

12

ฉันยังพยายามทำให้ไดอะล็อกแฟรกเมนต์ของฉันแสดงด้วยธีมที่แตกต่างกันสำหรับกิจกรรมของมันและทำตามวิธีแก้ปัญหานี้ เช่นเดียวกับบางคนที่กล่าวถึงในความคิดเห็นฉันไม่สามารถใช้งานได้และกล่องโต้ตอบยังคงแสดงตามธีมที่ระบุในไฟล์ Manifest ปัญหาที่เกิดขึ้นจะกลายเป็นว่าผมสร้างโต้ตอบโดยใช้AlertDialog.BuilderในonCreateDialogวิธีการและอื่น ๆ ก็ไม่ได้ทำให้การใช้onCreateViewวิธีการตามที่ปรากฏในคำตอบที่ผมเชื่อมโยงกับ และเมื่อฉันสร้างอินสแตนซ์AlertDialog.Builderฉันกำลังส่งในบริบทโดยใช้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จากนั้นให้ตรวจสอบบริบทที่ส่งผ่านไปยังตัวสร้าง หวังว่านี่จะช่วยได้! :)


9

ตรวจสอบให้แน่ใจว่าคุณได้android:minSdkVersion="11"ตั้งค่าไว้ในรายการของคุณ นี่อาจเป็นสาเหตุว่าทำไมเดวิดตัวอย่างไม่เหมาะกับคุณ

ตั้งค่าandroid:theme="@android:style/Theme.Holo.Light"แอตทริบิวต์สำหรับ<application>แท็กไม่ใช่ไฟล์<activity>แท็ก

ปัญหาที่เป็นไปได้อีกประการหนึ่งอาจเป็นวิธีที่คุณได้รับบริบทเมื่อใช้ไฟล์ContextThemeWrapper(). หากคุณใช้บางอย่างเช่นgetActivity().getApplicationContext()เพียงแค่แทนที่ด้วยgetActivity()แทน

โดยปกติ Theme.Holo ควรใช้กับ Fragments ที่เชื่อมโยงกับ MainActivity

โปรดทราบว่าคุณใช้ ContextThemeWrapper เมื่อคุณต้องการที่จะใช้ที่แตกต่างกันธีมสำหรับส่วนของคุณ อาจช่วยได้หากคุณให้ชิ้นส่วนของโค้ดจาก MainActivity ซึ่งคุณเพิ่ม Fragments ของคุณ


ลิงค์ที่มีประโยชน์:

Custom ListView ใน Fragment ไม่ยึดติดกับธีมหลัก


8

ฉันลองวิธีแก้ปัญหาที่ David แนะนำว่ามันใช้งานได้ แต่ไม่ใช่ในทุกสถานการณ์:
1. สำหรับส่วนแรกที่เพิ่มลงในสแต็กนั้นมีธีมของกิจกรรมไม่ใช่ธีมที่กำหนดไว้ใน onCrateView แต่ในส่วนที่สองที่ฉัน เพิ่มลงในสแต็กแก้ไขให้ถูกต้องใช้กับชิ้นส่วน

2. ในส่วนที่สองที่แสดงอย่างถูกต้องฉันได้ทำสิ่งต่อไปนี้ฉันบังคับให้ปิดแอปโดยการล้างหน่วยความจำเปิดแอปอีกครั้งและเมื่อกิจกรรมถูกสร้างขึ้นใหม่ด้วยส่วนที่แยกส่วนนั้นเปลี่ยนสิ่งที่ผิดไปจาก กิจกรรมและไม่เหมือนกันที่ตั้งค่าไว้ใน onCrateView ของแฟรกเมนต์

เพื่อแก้ไขปัญหาฉันได้ทำการเปลี่ยนแปลงเล็กน้อยและแทนที่อาร์กิวเมนต์ container จาก inflater.inflate ด้วย null

ฉันไม่รู้วิธีที่ Inflater ใช้ในบางสถานการณ์บริบทจากมุมมองคอนเทนเนอร์

หมายเหตุ - ฉันใช้ 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);
} 

ขอบคุณโซลูชันของคุณช่วยฉันได้ ธีมของ DialogFragment ของฉันแตกต่างจากส่วนอื่น ๆ มันทำให้ EditText ของฉันแปลก ๆ เพราะฉันใช้ inflater ที่ให้มา ฉันพบความช่วยเหลือบางอย่างเกี่ยวกับ ContextThemeWrapper แต่พวกเขาไม่ได้แสดงให้เห็นว่ามันทำอย่างไร โซลูชันของคุณใช้ได้ผลสำหรับฉัน ขอบคุณมาก.
พวงดาว

4

ฉันรู้ว่ามันช้าไปหน่อย แต่มันอาจช่วยคนอื่นได้เพราะสิ่งนี้ช่วยฉันได้นอกจากนี้คุณยังสามารถลองเพิ่มบรรทัดของโค้ดด้านล่างภายในฟังก์ชัน onCreatView ของแฟรกเมนต์

    inflater.context.setTheme(R.style.yourCustomTheme)

2

สร้างคลาส java จากนั้นใช้เค้าโครงที่คุณต้องการเปลี่ยนธีมในเมธอด onCreate จากนั้นพูดถึงมันในรายการตามปกติ


1

หากคุณต้องการใช้สไตล์สำหรับส่วนเฉพาะให้เพิ่มบรรทัดนี้ก่อนที่จะเรียกonCreateView()หรือonAttach()ส่วนย่อย

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

และหากคุณต้องการที่จะตั้งแถบสถานะโปร่งใสจากนั้นตั้งค่าเป็นเท็จเพื่อ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

คุณสามารถลองใช้ lollipop ได้ใน onAttach

หน้าต่างสุดท้าย = activity.getWindow (); window.setStatusBarColor (myStatusBarColor)

และตั้งค่ากลับเป็นค่าเริ่มต้นใน ondettach

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.