จะเปลี่ยนสีพื้นหลังของเมนูตัวเลือกได้อย่างไร?


99

ฉันกำลังพยายามเปลี่ยนสีเริ่มต้นสำหรับเมนูตัวเลือกซึ่งเป็นสีขาว: ฉันต้องการพื้นหลังสีดำสำหรับทุกรายการในเมนูตัวเลือก

ฉันได้ลองถ่ายบางอย่างเช่น android: itemBackground = "# 000000" ในองค์ประกอบรายการภายในองค์ประกอบเมนู แต่ไม่ได้ผล

ฉันจะทำสิ่งนี้ให้สำเร็จได้อย่างไร?



วิธีที่ดีที่สุดอยู่ที่นี่ stackoverflow.com/questions/3519277/…
user3156040

คำตอบ:


66

หลังจากใช้เวลานานพอสมควรในการลองใช้ตัวเลือกทั้งหมดวิธีเดียวที่ฉันจะได้รับแอปโดยใช้ AppCompat v7 เพื่อเปลี่ยนพื้นหลังของเมนูล้นคือการใช้แอตทริบิวต์ itemBackground:

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    ...
    <item name="android:itemBackground">@color/overflow_background</item>
    ...
</style>

ทดสอบจาก API 4.2 ถึง 5.0


2
นี่ควรเป็นคำตอบที่ยอมรับง่ายและเรียบง่าย
Alex Ardavin

4
แต่สิ่งนี้จะลบเอฟเฟกต์ระลอกคลื่น: / อย่างไรก็ตามจะนำมันกลับมา?
David Velasquez

ฉันต้องการเปลี่ยนพื้นหลังของหน้าต่างทั้งหมดไม่ใช่พื้นหลังรายการแยกกันเช่นจะเพิ่มเส้นขอบให้กับแต่ละรายการหากคุณตั้งค่าพื้นหลังดังกล่าว (มีเส้นขอบ) ...
user25

1
สีข้อความของเมนูเป็นอย่างไร
doctorram

51

นี่เป็นปัญหาที่เห็นได้ชัดว่าโปรแกรมเมอร์จำนวนมากมีและ Google ยังไม่ได้จัดหาโซลูชันที่น่าพอใจและได้รับการสนับสนุน

มีความตั้งใจและความเข้าใจผิดมากมายที่ลอยอยู่รอบ ๆ โพสต์ในหัวข้อนี้ดังนั้นโปรดอ่านคำตอบทั้งหมดนี้ก่อนตอบกลับ

ด้านล่างนี้ฉันมีการแฮ็กเวอร์ชันที่ "ละเอียด" และแสดงความคิดเห็นเป็นอย่างดีจากคำตอบอื่น ๆ ในหน้านี้รวมถึงแนวคิดจากคำถามที่เกี่ยวข้องอย่างละเอียด

เปลี่ยนสีพื้นหลังของเมนู Android

จะเปลี่ยนสีพื้นหลังของเมนูตัวเลือกได้อย่างไร?

Android: ปรับแต่งเมนูของแอปพลิเคชัน (เช่นสีพื้นหลัง)

http://www.macadamian.com/blog/post/android_-_theming_the_unthemable/

ปุ่มสลับเมนูรายการของ Android

เป็นไปได้ไหมที่จะทำให้พื้นหลังของเมนูตัวเลือก Android ไม่โปร่งแสง?

http://www.codeproject.com/KB/android/AndroidMenusMyWay.aspx

การตั้งค่าพื้นหลังของเมนูให้ทึบแสง

ฉันทดสอบแฮ็คนี้บน 2.1 (เครื่องจำลอง), 2.2 (อุปกรณ์จริง 2 เครื่อง) และ 2.3 (อุปกรณ์จริง 2 เครื่อง) ฉันยังไม่มีแท็บเล็ต 3.X ที่จะทดสอบ แต่จะโพสต์การเปลี่ยนแปลงที่จำเป็นที่นี่เมื่อ / ถ้าฉันทำ เนื่องจากแท็บเล็ต 3.X ใช้แถบการดำเนินการแทนเมนูตัวเลือกตามที่อธิบายไว้ที่นี่:

http://developer.android.com/guide/topics/ui/menus.html#options-menu

แฮ็คนี้แทบจะไม่ทำอะไรเลย (ไม่เป็นอันตรายและไม่ดี) บนแท็บเล็ต 3.X

ข้อความของปัญหา (อ่านสิ่งนี้ก่อนที่จะตอบกลับด้วยความคิดเห็นเชิงลบ):

เมนูตัวเลือกมีรูปแบบที่แตกต่างกันอย่างมากในอุปกรณ์ต่างๆ สีดำบริสุทธิ์พร้อมข้อความสีขาวในบางส่วนมีสีขาวบริสุทธิ์และมีข้อความสีดำอยู่ในบางส่วน ผมและนักพัฒนาอื่น ๆ อีกมากมายต้องการที่จะควบคุมสีพื้นหลังของตัวเลือกเมนูเซลล์เช่นเดียวกับสีของข้อความเมนูตัวเลือก

นักพัฒนาแอปบางรายต้องตั้งค่าสีพื้นหลังของเซลล์เท่านั้น (ไม่ใช่สีข้อความ) และสามารถทำได้อย่างสะอาดตายิ่งขึ้นโดยใช้สไตล์ android: panelFullBackground ที่อธิบายไว้ในคำตอบอื่น อย่างไรก็ตามขณะนี้ยังไม่มีวิธีควบคุมสีข้อความของเมนูตัวเลือกด้วยสไตล์ดังนั้นเราจึงสามารถใช้วิธีนี้เพื่อเปลี่ยนพื้นหลังเป็นสีอื่นที่จะไม่ทำให้ข้อความ "หายไป"

เราชอบที่จะทำสิ่งนี้ด้วยโซลูชันที่มีเอกสารและพิสูจน์ได้ในอนาคต แต่โซลูชันนี้ไม่สามารถใช้ได้กับ Android <= 2.3 ดังนั้นเราจึงต้องใช้โซลูชันที่ใช้งานได้ในเวอร์ชันปัจจุบันและได้รับการออกแบบมาเพื่อลดโอกาสที่จะเกิดปัญหา / เสียหายในเวอร์ชันต่อ ๆ ไป เราต้องการวิธีแก้ปัญหาที่ล้มเหลวอย่างสง่างามกลับสู่พฤติกรรมเริ่มต้นหากต้องล้มเหลว

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

มีข้อบกพร่องของ Google Android ที่โพสต์เกี่ยวกับเรื่องนี้: โปรดเพิ่มการสนับสนุนของคุณด้วยการติดดาวข้อบกพร่องนี้ (โปรดทราบว่า Google ไม่สนับสนุนความคิดเห็น "ฉันด้วย" เพียงแค่ติดดาวก็เพียงพอแล้ว):

http://code.google.com/p/android/issues/detail?id=4441

สรุปวิธีแก้ปัญหาให้ไกล:

ผู้โพสต์หลายคนแนะนำการแฮ็กที่เกี่ยวข้องกับ LayoutInflater.Factory การแฮ็กที่แนะนำใช้งานได้กับ Android <= 2.2 และล้มเหลวสำหรับ Android 2.3 เนื่องจากการแฮ็กสร้างข้อสันนิษฐานที่ไม่มีเอกสารว่าสามารถเรียก LayoutInflater.getView () ได้โดยตรงโดยไม่ต้องอยู่ในการเรียก LayoutInflater.inflate () บนอินสแตนซ์ LayoutInflater เดียวกัน โค้ดใหม่ใน Android 2.3 ทำลายสมมติฐานนี้และนำไปสู่ ​​NullPointerException

แฮ็คที่ได้รับการขัดเกลาเล็กน้อยของฉันด้านล่างไม่ได้อาศัยสมมติฐานนี้

นอกจากนี้แฮ็คยังอาศัยการใช้ชื่อคลาสภายในที่ไม่มีเอกสาร "com.android.internal.view.menu.IconMenuItemView" เป็นสตริง (ไม่ใช่ประเภท Java) ฉันไม่เห็นวิธีใดที่จะหลีกเลี่ยงปัญหานี้และยังคงบรรลุเป้าหมายที่ระบุไว้ อย่างไรก็ตามเป็นไปได้ที่จะทำการแฮ็คด้วยความระมัดระวังซึ่งจะถอยกลับหาก "com.android.internal.view.menu.IconMenuItemView" ไม่ปรากฏในระบบปัจจุบัน

โปรดเข้าใจอีกครั้งว่านี่เป็นการแฮ็กและฉันไม่ได้อ้างว่าสิ่งนี้ใช้ได้กับทุกแพลตฟอร์ม แต่พวกเรานักพัฒนาไม่ได้อยู่ในโลกวิชาการแฟนตาซีที่ทุกอย่างต้องเป็นไปตามหนังสือ: เรามีปัญหาที่ต้องแก้และเราต้องแก้ไขให้ดีที่สุดเท่าที่จะทำได้ ตัวอย่างเช่นดูเหมือนว่าไม่น่าเป็นไปได้ที่ "com.android.internal.view.menu.IconMenuItemView" จะมีอยู่ในแท็บเล็ต 3.X เนื่องจากใช้แถบการดำเนินการแทนเมนูตัวเลือก

ในที่สุดนักพัฒนาบางคนได้แก้ไขปัญหานี้โดยการระงับเมนูตัวเลือก Android ทั้งหมดและเขียนคลาสเมนูของตัวเอง (ดูลิงก์ด้านบน) ฉันยังไม่ได้ลอง แต่ถ้าคุณมีเวลาเขียนมุมมองของคุณเองและหาวิธีแทนที่มุมมองของ Android (ฉันแน่ใจว่าปีศาจอยู่ในรายละเอียดที่นี่) อาจเป็นทางออกที่ดีที่ไม่ต้องการอะไรเลย แฮ็กที่ไม่มีเอกสาร

สับ:

นี่คือรหัส

ในการใช้รหัสนี้ให้เรียก addOptionsMenuHackerInflaterFactory () ONCE จากกิจกรรมของคุณ onCreate () หรือกิจกรรมของคุณ onCreateOptionsMenu () ตั้งค่าโรงงานเริ่มต้นที่จะส่งผลต่อการสร้างเมนูตัวเลือกในภายหลัง ไม่มีผลต่อเมนูตัวเลือกที่สร้างไว้แล้ว (แฮ็กก่อนหน้านี้ใช้ชื่อฟังก์ชันของ setMenuBackground () ซึ่งทำให้เข้าใจผิดอย่างมากเนื่องจากฟังก์ชันไม่ได้ตั้งค่าคุณสมบัติเมนูใด ๆ ก่อนที่จะกลับมา)

@SuppressWarnings("rawtypes")
static Class       IconMenuItemView_class = null;
@SuppressWarnings("rawtypes")
static Constructor IconMenuItemView_constructor = null;

// standard signature of constructor expected by inflater of all View classes
@SuppressWarnings("rawtypes")
private static final Class[] standard_inflater_constructor_signature = 
new Class[] { Context.class, AttributeSet.class };

protected void addOptionsMenuHackerInflaterFactory()
{
    final LayoutInflater infl = getLayoutInflater();

    infl.setFactory(new Factory()
    {
        public View onCreateView(final String name, 
                                 final Context context,
                                 final AttributeSet attrs)
        {
            if (!name.equalsIgnoreCase("com.android.internal.view.menu.IconMenuItemView"))
                return null; // use normal inflater

            View view = null;

            // "com.android.internal.view.menu.IconMenuItemView" 
            // - is the name of an internal Java class 
            //   - that exists in Android <= 3.2 and possibly beyond
            //   - that may or may not exist in other Android revs
            // - is the class whose instance we want to modify to set background etc.
            // - is the class we want to instantiate with the standard constructor:
            //     IconMenuItemView(context, attrs)
            // - this is what the LayoutInflater does if we return null
            // - unfortunately we cannot just call:
            //     infl.createView(name, null, attrs);
            //   here because on Android 3.2 (and possibly later):
            //   1. createView() can only be called inside inflate(),
            //      because inflate() sets the context parameter ultimately
            //      passed to the IconMenuItemView constructor's first arg,
            //      storing it in a LayoutInflater instance variable.
            //   2. we are inside inflate(),
            //   3. BUT from a different instance of LayoutInflater (not infl)
            //   4. there is no way to get access to the actual instance being used
            // - so we must do what createView() would have done for us
            //
            if (IconMenuItemView_class == null)
            {
                try
                {
                    IconMenuItemView_class = getClassLoader().loadClass(name);
                }
                catch (ClassNotFoundException e)
                {
                    // this OS does not have IconMenuItemView - fail gracefully
                    return null; // hack failed: use normal inflater
                }
            }
            if (IconMenuItemView_class == null)
                return null; // hack failed: use normal inflater

            if (IconMenuItemView_constructor == null)
            {
                try
                {
                    IconMenuItemView_constructor = 
                    IconMenuItemView_class.getConstructor(standard_inflater_constructor_signature);
                }
                catch (SecurityException e)
                {
                    return null; // hack failed: use normal inflater
                }
                catch (NoSuchMethodException e)
                {
                    return null; // hack failed: use normal inflater
                }
            }
            if (IconMenuItemView_constructor == null)
                return null; // hack failed: use normal inflater

            try
            {
                Object[] args = new Object[] { context, attrs };
                view = (View)(IconMenuItemView_constructor.newInstance(args));
            }
            catch (IllegalArgumentException e)
            {
                return null; // hack failed: use normal inflater
            }
            catch (InstantiationException e)
            {
                return null; // hack failed: use normal inflater
            }
            catch (IllegalAccessException e)
            {
                return null; // hack failed: use normal inflater
            }
            catch (InvocationTargetException e)
            {
                return null; // hack failed: use normal inflater
            }
            if (null == view) // in theory handled above, but be safe... 
                return null; // hack failed: use normal inflater


            // apply our own View settings after we get back to runloop
            // - android will overwrite almost any setting we make now
            final View v = view;
            new Handler().post(new Runnable()
            {
                public void run()
                {
                    v.setBackgroundColor(Color.BLACK);

                    try
                    {
                        // in Android <= 3.2, IconMenuItemView implemented with TextView
                        // guard against possible future change in implementation
                        TextView tv = (TextView)v;
                        tv.setTextColor(Color.WHITE);
                    }
                    catch (ClassCastException e)
                    {
                        // hack failed: do not set TextView attributes
                    }
                }
            });

            return view;
        }
    });
}

ขอบคุณที่อ่านและสนุก!


15
สิ่งเดียวที่ฉันเชื่อถือได้เมื่อพยายามใช้ (และวิธีแก้ปัญหาที่คล้ายกัน) คือ "java.lang.IllegalStateException: โรงงานได้ถูกตั้งค่าไว้แล้วใน LayoutInflater`
Bostone

ใช้ได้ผลสำหรับฉัน! ในที่สุดก็มีทางออก! ทดสอบกับ Gingerbread, Honeycomb และ ICS
Chad Schultz

ทดสอบใน Samsung Galaxy Nexus (4.1.1) แล้วใช้งานได้! ของดีหลุยส์!
Felipe Caldas

2
ใช้งานได้กับ Galaxy Nexus 7 (4.1.1) แต่สีของข้อความจะถูกเปลี่ยนกลับสำหรับทุก ๆ การโทรไปยังเมนูที่ตามมาหลังจากถูกซ่อนเป็นครั้งแรก
Will Kru

1
ฉันได้รับ IllegalStateException ด้วย ดูเหมือนว่าแฮ็คจะเข้ากันไม่ได้กับ ActionBarSherlock ที่ฉันใช้อยู่
Travis

20

android:panelFullBackgroundแอตทริบิวต์สไตล์สำหรับพื้นหลังเมนู

แม้ว่าเอกสารจะระบุว่าอย่างไร แต่ก็ต้องเป็นทรัพยากร (เช่น@android:color/blackหรือ@drawable/my_drawable) แต่จะผิดพลาดหากคุณใช้ค่าสีโดยตรง

นอกจากนี้ยังจะกำจัดขอบรายการที่ฉันไม่สามารถเปลี่ยนหรือลบโดยใช้โซลูชันของ primalpop

สำหรับสีข้อความฉันไม่พบวิธีตั้งค่าผ่านสไตล์ใน 2.2 และฉันแน่ใจว่าฉันได้ลองทุกอย่างแล้ว (ซึ่งเป็นวิธีที่ฉันค้นพบแอตทริบิวต์พื้นหลังเมนู) คุณจะต้องใช้วิธีแก้ปัญหาของ primalpop สำหรับสิ่งนั้น


3
ต้องตั้งค่านี้ที่ไหน ฉันไม่สามารถทำให้มันทำงานบน Android 2.2 หรือ 2.3
Janusz

1
@ Janusz ใน Styles.xml สิ่งนี้อาจช่วยได้: developer.android.com/guide/topics/resources/…
Pilot_51

2
ไม่ได้ผลดีถ้าคุณสามารถระบุว่าควรจะไปที่ไหนลองทุกที่ยกเว้นการสร้างรูปแบบอื่นสำหรับรายการเมนูของฉันเพื่อระบุคุณลักษณะ .....
John

14

สำหรับ Android 2.3 สามารถทำได้ด้วยการแฮ็กที่หนักมาก:

สาเหตุหลักของปัญหาเกี่ยวกับ Android 2.3 คือใน LayoutInflater mConstructorArgs [0] = mContext จะถูกตั้งค่าระหว่างการเรียกใช้เพื่อ

http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.3.3_r1/android/view/LayoutInflater.java/#352

protected void setMenuBackground(){

    getLayoutInflater().setFactory( new Factory() {

        @Override
        public View onCreateView (final String name, final Context context, final AttributeSet attrs ) {

            if ( name.equalsIgnoreCase( "com.android.internal.view.menu.IconMenuItemView" ) ) {

                try { // Ask our inflater to create the view
                    final LayoutInflater f = getLayoutInflater();
                    final View[] view = new View[1]:
                    try {
                        view[0] = f.createView( name, null, attrs );
                    } catch (InflateException e) {
                        hackAndroid23(name, attrs, f, view);
                    }
                    // Kind of apply our own background
                    new Handler().post( new Runnable() {
                        public void run () {
                            view.setBackgroundResource( R.drawable.gray_gradient_background);
                        }
                    } );
                    return view;
                }
                catch ( InflateException e ) {
                }
                catch ( ClassNotFoundException e ) {
                }
            }
            return null;
        }
    });
}

static void hackAndroid23(final String name,
    final android.util.AttributeSet attrs, final LayoutInflater f,
    final TextView[] view) {
    // mConstructorArgs[0] is only non-null during a running call to inflate()
    // so we make a call to inflate() and inside that call our dully XmlPullParser get's called
    // and inside that it will work to call "f.createView( name, null, attrs );"!
    try {
        f.inflate(new XmlPullParser() {
            @Override
            public int next() throws XmlPullParserException, IOException {
                try {
                    view[0] = (TextView) f.createView( name, null, attrs );
                } catch (InflateException e) {
                } catch (ClassNotFoundException e) {
                }
                throw new XmlPullParserException("exit");
            }   
        }, null, false);
    } catch (InflateException e1) {
        // "exit" ignored
    }
}

ฉันทดสอบแล้วว่าทำงานบน Android 2.3 และยังคงใช้งานได้กับเวอร์ชันก่อนหน้า หากมีสิ่งใดขัดข้องใน Android เวอร์ชันใหม่ ๆ คุณจะเห็นรูปแบบเมนูเริ่มต้นแทน


รหัสนี้ทำงานได้ถึงเวอร์ชัน 2.1 เท่านั้นรหัสนี้ดูเหมือนจะดีกว่า: stackoverflow.com/questions/2944244/…
Felipe Caldas

สวัสดีฉันใช้ฟังก์ชั่นของคุณแล้ว แต่ฉันได้รับข้อผิดพลาดต่อไปนี้เกิดข้อผิดพลาดพองคลาส com.android.internal.view.menu.IconMenuItemViewแล้วข้อยกเว้นอีกข้อผิดพลาดพองคลาส <unknown> ... ตอนนี้ฉันควรทำอย่างไรตอนนี้ ... เหรอ? โปรดช่วยฉัน
Rushabh Patel

13

เพิ่งพบปัญหานี้เช่นกันในแอปที่ต้องเข้ากันได้กับ Gingerbread และยังคงรูปแบบจากอุปกรณ์ที่เปิดใช้งาน Holo ให้ได้มากที่สุด

ฉันพบวิธีแก้ปัญหาที่ค่อนข้างสะอาดซึ่งใช้ได้ผลสำหรับฉัน

ในธีมฉันใช้พื้นหลังแบบวาดได้ 9 แพทช์เพื่อให้ได้สีพื้นหลังที่กำหนดเอง

<style name="Theme.Styled" parent="Theme.Sherlock">
   ...
   <item name="android:panelFullBackground">@drawable/menu_hardkey_panel</item>
</style>

ฉันเลิกพยายามจัดรูปแบบสีข้อความและใช้ Spannable เพื่อตั้งค่าสีข้อความสำหรับรายการของฉันในรหัส:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
   MenuInflater inflater = getSupportMenuInflater();
   inflater.inflate(R.menu.actions_main, menu);

   if (android.os.Build.VERSION.SDK_INT < 
        android.os.Build.VERSION_CODES.HONEYCOMB) {

        SpannableStringBuilder text = new SpannableStringBuilder();
        text.append(getString(R.string.action_text));
        text.setSpan(new ForegroundColorSpan(Color.WHITE), 
                0, text.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

        MenuItem item1 = menu.findItem(R.id.action_item1);
        item1.setTitle(text);
   }

   return true;
}

ใช้งานได้ดีสำหรับปัญหาของฉันในการใช้ธีม ActionBarSherlock Light บนอุปกรณ์ Gingerbread! ด้วยวิธีนี้ฉันสามารถเปลี่ยนพื้นหลังของเมนูตัวเลือกเป็นสีเทาอ่อนและสีข้อความเป็นสีดำได้อย่างง่ายดาย (ไอคอนเป็นสีดำอยู่แล้วเช่นเดียวกับใน ActionBar ขอบคุณ!
florianbaethge

13

นี่คือวิธีที่ฉันแก้ไขของฉัน ฉันเพิ่งระบุสีพื้นหลังและสีข้อความในสไตล์ เช่นไฟล์ res> values> styles.xml

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="android:itemBackground">#ffffff</item>
    <item name="android:textColor">#000000</item>
</style>

1
textcolor จะเปลี่ยนไปทุกที่
user3156040

มันแค่เปลี่ยนสีพื้นหลังของรายการและเค้าโครงตัวเลือกเมนูมีช่องว่างด้านบนและด้านล่างมันไม่ได้ช่วยอะไร
FarshidABZ

10

สิ่งหนึ่งที่ควรทราบว่าพวกคุณกำลังทำให้ปัญหาซับซ้อนมากเกินไปเช่นเดียวกับโพสต์อื่น ๆ ! สิ่งที่คุณต้องทำคือสร้างตัวเลือกที่วาดได้ด้วยพื้นหลังที่คุณต้องการและตั้งค่าเป็นรายการจริง ฉันใช้เวลาเพียงสองชั่วโมงในการลองใช้วิธีแก้ปัญหาของคุณ (แนะนำทั้งหมดในหน้านี้) และไม่มีวิธีใดได้ผล ไม่ต้องพูดถึงว่ามีข้อผิดพลาดมากมายที่ทำให้ประสิทธิภาพของคุณช้าลงในบล็อก try / catch ที่คุณมี

อย่างไรก็ตามนี่คือไฟล์ xml เมนู:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/m1"
          android:icon="@drawable/item1_selector"
          />
    <item android:id="@+id/m2"
          android:icon="@drawable/item2_selector"
          />
</menu>

ตอนนี้ใน item1_selector ของคุณ:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true" android:drawable="@drawable/item_highlighted" />
    <item android:state_selected="true" android:drawable="@drawable/item_highlighted" />
    <item android:state_focused="true" android:drawable="@drawable/item_nonhighlighted" />
    <item android:drawable="@drawable/item_nonhighlighted" />
</selector>

ครั้งต่อไปที่คุณตัดสินใจไปซูเปอร์มาร์เก็ตผ่านแคนาดาลองใช้ Google Maps!


ฉันเห็นด้วยอย่างยิ่ง ทำไมต้องคิดค้น Android ใหม่เมื่อเขา =) มีอยู่แล้ว?
Fredrik

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

53
สวยงามสง่างามและไม่สามารถแก้ไขปัญหาได้ทั้งหมด
Aea

1
ถ้าจำไม่ผิดนี่ก็แค่เปลี่ยนพื้นหลังของไอคอนไม่ใช่รายการเมนูเองที่ยังคงเป็นสีขาว
ตั้งแต่

3
นี่ไม่ใช่คำถาม นี่คือความคิดที่แตกต่างกันโดยสิ้นเชิง
Kostadin

4
 <style name="AppThemeLight" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="android:itemBackground">#000000</item>
</style>

มันใช้ได้ดีสำหรับฉัน


วิธีเปลี่ยนสีชื่อ
parvez rafi

3
    /* 
     *The Options Menu (the one that pops up on pressing the menu button on the emulator) 
     * can be customized to change the background of the menu 
     *@primalpop  
   */ 

    package com.pop.menu;

    import android.app.Activity;
    import android.content.Context;
    import android.os.Bundle;
    import android.os.Handler;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.InflateException;
    import android.view.LayoutInflater;
    import android.view.Menu;
    import android.view.MenuInflater;
    import android.view.View;
    import android.view.LayoutInflater.Factory;

    public class Options_Menu extends Activity {

        private static final String TAG = "DEBUG";

        /** Called when the activity is first created. */
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);

        }

        /* Invoked when the menu button is pressed */

        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            // TODO Auto-generated method stub
            super.onCreateOptionsMenu(menu);
            MenuInflater inflater = new MenuInflater(getApplicationContext());
            inflater.inflate(R.menu.options_menu, menu);
            setMenuBackground();
            return true;
        }

        /*IconMenuItemView is the class that creates and controls the options menu 
         * which is derived from basic View class. So We can use a LayoutInflater 
         * object to create a view and apply the background.
         */
        protected void setMenuBackground(){

            Log.d(TAG, "Enterting setMenuBackGround");
            getLayoutInflater().setFactory( new Factory() {

                @Override
                public View onCreateView ( String name, Context context, AttributeSet attrs ) {

                    if ( name.equalsIgnoreCase( "com.android.internal.view.menu.IconMenuItemView" ) ) {

                        try { // Ask our inflater to create the view
                            LayoutInflater f = getLayoutInflater();
                            final View view = f.createView( name, null, attrs );
                            /* 
                             * The background gets refreshed each time a new item is added the options menu. 
                             * So each time Android applies the default background we need to set our own 
                             * background. This is done using a thread giving the background change as runnable
                             * object
                             */
                            new Handler().post( new Runnable() {
                                public void run () {
                                    view.setBackgroundResource( R.drawable.background);
                                }
                            } );
                            return view;
                        }
                        catch ( InflateException e ) {}
                        catch ( ClassNotFoundException e ) {}
                    }
                    return null;
                }
            });
        }
    }

3
โปรดอย่าทำสิ่งนี้: name.equalsIgnoreCase ("com.android.internal.view.menu.IconMenuItemView" ตามชื่อที่ระบุไว้อย่างชัดเจนว่านี่เป็นการใช้รายละเอียดการใช้งานส่วนตัวดังนั้นจึงสามารถทำลายการอัปเดตแพลตฟอร์มหรืออุปกรณ์ใด ๆ
hackbod

1
IconMenuItemView เป็นคลาสที่สร้างและควบคุมเมนูตัวเลือกซึ่งได้มาจากคลาส View พื้นฐาน คลาสนี้มาจากซอร์สโค้ดของ Android และมีมาตั้งแต่อย่างน้อย api เวอร์ชัน 5 ฉันไม่เห็นว่ามันทำลายการอัปเดตแพลตฟอร์มหรืออุปกรณ์ใด ๆ
ปฐมกัปปะจันทร์

1
คุณมองไม่เห็นเพราะมองไม่เห็นอนาคต แม้ว่าจะมีวิธีที่จะแน่ใจได้ แต่ก็เป็นการปฏิบัติที่ไม่ดี
HXCaine

ขอบคุณสิ่งนี้มีประโยชน์ในการหยิก อย่างไรก็ตามไม่ทำงานในกรณีพิเศษเช่นรายการที่สร้างใน onCreateOptionsMenu แต่ปิดใช้งานใน onPrepareOptionsMenu แต่หลังจากนั้นจึงเปิดใช้งานอีกครั้ง
HRJ

3

ขอบคุณมาร์คัส! ทำงานบน 2.3 ได้อย่างราบรื่นโดยแก้ไขข้อผิดพลาดทางไวยากรณ์นี่คือรหัสคงที่

    protected void setMenuBackground() {
    getLayoutInflater().setFactory(new Factory() {

        @Override
        public View onCreateView(final String name, final Context context,
                final AttributeSet attrs) {

            if (name.equalsIgnoreCase("com.android.internal.view.menu.IconMenuItemView")) {

                try { // Ask our inflater to create the view
                    final LayoutInflater f = getLayoutInflater();
                    final View[] view = new View[1];
                    try {
                        view[0] = f.createView(name, null, attrs);
                    } catch (InflateException e) {
                        hackAndroid23(name, attrs, f, view);
                    }
                    // Kind of apply our own background
                    new Handler().post(new Runnable() {
                        public void run() {
                            view[0].setBackgroundColor(Color.WHITE);

                        }
                    });
                    return view[0];
                } catch (InflateException e) {
                } catch (ClassNotFoundException e) {

                }
            }
            return null;
        }
    });
}

static void hackAndroid23(final String name,
        final android.util.AttributeSet attrs, final LayoutInflater f,
        final View[] view) {
    // mConstructorArgs[0] is only non-null during a running call to
    // inflate()
    // so we make a call to inflate() and inside that call our dully
    // XmlPullParser get's called
    // and inside that it will work to call
    // "f.createView( name, null, attrs );"!
    try {
        f.inflate(new XmlPullParser() {
            @Override
            public int next() throws XmlPullParserException, IOException {
                try {
                    view[0] = (TextView) f.createView(name, null, attrs);
                } catch (InflateException e) {
                } catch (ClassNotFoundException e) {
                }
                throw new XmlPullParserException("exit");
            }
        }, null, false);
    } catch (InflateException e1) {
        // "exit" ignored
    }
}

1
ทั้งหมดที่ฉันได้รับจากสิ่งนี้: java.lang.IllegalStateException: โรงงานได้ถูกตั้งค่าไว้แล้วใน LayoutInflater นี้
Bostone

เพื่อให้ทำงานร่วมกับ ActionBarSherlock และกรอบความเข้ากันได้และหลีกเลี่ยง IllegalStateException ดูเคล็ดลับนี้stackoverflow.com/questions/13415284/…
avianey

3
protected void setMenuBackground() {
    getLayoutInflater().setFactory(new Factory() {
        @Override
        public View onCreateView (String name, Context context, AttributeSet attrs) {
            if (name.equalsIgnoreCase("com.android.internal.view.menu.IconMenuItemView")) {
                try {
                    // Ask our inflater to create the view
                    LayoutInflater f = getLayoutInflater();
                    final View view = f.createView(name, null, attrs);
                    // Kind of apply our own background
                    new Handler().post( new Runnable() {
                        public void run () {
                            view.setBackgroundResource(R.drawable.gray_gradient_background);
                        }
                    });
                    return view;
                }
                catch (InflateException e) {
                }
                catch (ClassNotFoundException e) {
                }
            }
            return null;
        }
    });
}

นี่คือไฟล์ XML

gradient 
    android:startColor="#AFAFAF" 
    android:endColor="#000000"
    android:angle="270"
shape

1

androidxหากคุณต้องการตั้งค่าสีโดยพลการนี้ดูเหมือนจะทำงานค่อนข้างดีสำหรับ ทดสอบกับ KitKat และ Pie ใส่สิ่งนี้ลงในAppCompatActivity:

@Override public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
    if (name.equals("androidx.appcompat.view.menu.ListMenuItemView") &&
            parent.getParent() instanceof FrameLayout) {
            ((View) parent.getParent()).setBackgroundColor(yourFancyColor);
    }
    return super.onCreateView(parent, name, context, attrs);
}

สิ่งนี้จะกำหนดสีandroid.widget.PopupWindow$PopupBackgroundViewซึ่งตามที่คุณอาจเดาได้คือวาดสีพื้นหลัง ไม่มีการวาดทับและคุณสามารถใช้สีกึ่งโปร่งใสได้เช่นกัน

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