Làm cách nào để thay đổi màu nền của menu tùy chọn?


96

Tôi đang cố gắng thay đổi màu mặc định cho menu tùy chọn là màu trắng: Tôi muốn có nền đen cho mọi mục trên menu tùy chọn.

Tôi đã thử một số cảnh quay như android: itemBackground = "# 000000" trên phần tử item trong phần tử menu nhưng nó không hoạt động.

Làm thế nào tôi có thể thực hiện điều này?



Cách tốt nhất là ở đây stackoverflow.com/questions/3519277/…
user3156040

Câu trả lời:


65

Sau khi dành một khoảng thời gian đáng kể để thử tất cả các tùy chọn, cách duy nhất tôi có thể tải ứng dụng bằng AppCompat v7 để thay đổi nền menu mục bổ sung là sử dụng thuộc tính itemBackground:

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

Đã kiểm tra từ API 4.2 đến 5.0.


2
Đây phải là câu trả lời được chấp nhận, dễ dàng và đơn giản.
Alex Ardavin

3
Nhưng điều này loại bỏ hiệu ứng gợn sóng: / Dù sao để đặt nó trở lại?
David Velasquez

Tôi muốn thay đổi toàn bộ nền cửa sổ chứ không phải nền mục riêng biệt, ví dụ: nó sẽ thêm đường viền cho từng mục nếu bạn đặt nền như vậy (có đường viền) ...
user25

1
Còn màu văn bản menu thì sao?
doctorram

51

Đây rõ ràng là một vấn đề mà rất nhiều lập trình viên gặp phải và Google vẫn chưa đưa ra được giải pháp hỗ trợ thỏa đáng.

Có rất nhiều ý định và hiểu lầm trái chiều xung quanh các bài đăng về chủ đề này, vì vậy vui lòng đọc toàn bộ câu trả lời này trước khi trả lời.

Dưới đây, tôi bao gồm một phiên bản hack được "tinh chỉnh" hơn và được nhận xét tốt hơn từ các câu trả lời khác trên trang này, cũng kết hợp các ý tưởng từ các câu hỏi liên quan rất chặt chẽ sau:

Thay đổi màu nền của menu android

Làm cách nào để thay đổi màu nền của menu tùy chọn?

Android: tùy chỉnh menu của ứng dụng (ví dụ: màu nền)

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

Nút chuyển đổi menu mục của Android

Có thể làm cho nền menu tùy chọn Android không trong mờ không?

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

Đặt nền menu thành mờ

Tôi đã thử nghiệm bản hack này trên 2.1 (giả lập), 2.2 (2 thiết bị thực) và 2.3 (2 thiết bị thực). Tôi chưa có bất kỳ máy tính bảng 3.X nào để thử nghiệm nhưng sẽ đăng bất kỳ thay đổi cần thiết nào ở đây khi / nếu tôi làm như vậy. Cho rằng máy tính bảng 3.X sử dụng Thanh hành động thay vì Menu tùy chọn, như được giải thích ở đây:

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

hack này gần như chắc chắn sẽ không làm gì (không có hại và không có lợi) trên máy tính bảng 3.X.

BÁO CÁO VẤN ĐỀ (đọc phần này trước khi kích hoạt trả lời bằng nhận xét tiêu cực):

Menu Tùy chọn có rất nhiều kiểu khác nhau trên các thiết bị khác nhau. Màu đen tuyền với chữ trắng trên một số, màu trắng tinh với chữ đen trên một số. Tôi và nhiều nhà phát triển khác muốn kiểm soát màu nền của các ô menu Tùy chọn cũng như màu của văn bản menu Tùy chọn .

Một số nhà phát triển ứng dụng nhất định chỉ cần đặt màu nền của ô (không phải màu văn bản) và họ có thể thực hiện việc này theo cách rõ ràng hơn bằng cách sử dụng kiểu android: panelFullBackground được mô tả trong một câu trả lời khác. Tuy nhiên, hiện tại không có cách nào để kiểm soát màu văn bản trong menu Tùy chọn với các kiểu và vì vậy người ta chỉ có thể sử dụng phương pháp này để thay đổi nền thành một màu khác mà không làm cho văn bản "biến mất".

Chúng tôi rất thích làm điều này với một giải pháp được ghi chép lại, phù hợp với tương lai, nhưng một giải pháp đơn giản là không khả dụng với Android <= 2.3. Vì vậy, chúng tôi phải sử dụng một giải pháp hoạt động trong các phiên bản hiện tại và được thiết kế để giảm thiểu khả năng bị rơi / hỏng trong các phiên bản sau. Chúng tôi muốn một giải pháp không thành công trở lại hành vi mặc định nếu nó phải thất bại.

Có nhiều lý do chính đáng khiến người ta có thể cần phải kiểm soát giao diện của các menu Tùy chọn (thường là để phù hợp với phong cách trực quan cho phần còn lại của ứng dụng) vì vậy tôi sẽ không đề cập đến điều đó.

Có một lỗi Google Android được đăng về điều này: vui lòng thêm hỗ trợ của bạn bằng cách gắn dấu sao cho lỗi này (lưu ý Google không khuyến khích nhận xét "tôi cũng vậy": chỉ cần một dấu sao là đủ):

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

TÓM TẮT CÁC GIẢI PHÁP SO FAR:

Một số áp phích đã đề xuất một vụ hack liên quan đến LayoutInflater.Factory. Bản hack được đề xuất đã hoạt động cho Android <= 2.2 và không thành công cho Android 2.3 vì bản hack đưa ra một giả định không có giấy tờ: rằng người ta có thể gọi LayoutInflater.getView () trực tiếp mà không cần hiện trong lệnh gọi LayoutInflater.inflate () trên cùng một phiên bản LayoutInflater. Mã mới trong Android 2.3 đã phá vỡ giả định này và dẫn đến NullPointerException.

Bản hack được tinh chỉnh một chút của tôi dưới đây không dựa trên giả định này.

Hơn nữa, các cuộc tấn công cũng dựa vào việc sử dụng tên lớp nội bộ, không có giấy tờ "com.android.internal.view.menu.IconMenuItemView" dưới dạng một chuỗi (không phải là kiểu Java). Tôi không thấy có cách nào để tránh điều này mà vẫn hoàn thành mục tiêu đã nêu. Tuy nhiên, có thể thực hiện vụ hack một cách cẩn thận sẽ rơi vào trường hợp "com.android.internal.view.menu.IconMenuItemView" không xuất hiện trên hệ thống hiện tại.

Một lần nữa, hãy hiểu rằng đây là một vụ hack và không có nghĩa là tôi khẳng định điều này sẽ hoạt động trên tất cả các nền tảng. Nhưng chúng tôi, các nhà phát triển không sống trong một thế giới học thuật giả tưởng, nơi mọi thứ đều phải như sách: chúng tôi có một vấn đề cần giải quyết và chúng tôi phải giải quyết nó tốt nhất có thể. Ví dụ: có vẻ như "com.android.internal.view.menu.IconMenuItemView" sẽ tồn tại trên máy tính bảng 3.X vì chúng sử dụng Thanh hành động thay vì Menu tùy chọn.

Cuối cùng, một số nhà phát triển đã giải quyết vấn đề này bằng cách loại bỏ hoàn toàn Menu tùy chọn Android và viết lớp menu của riêng họ (xem một số liên kết ở trên). Tôi chưa thử điều này, nhưng nếu bạn có thời gian để viết Chế độ xem của riêng mình và tìm ra cách thay thế chế độ xem của Android (tôi chắc chắn là quỷ ám trong chi tiết ở đây) thì đó có thể là một giải pháp tốt mà không cần bất kỳ hack không có giấy tờ.

GIAN LẬN:

Đây là mã.

Để sử dụng mã này, hãy gọi addOptionsMenuHackerInflaterFactory () MỘT LẦN từ hoạt động onCreate () hoặc hoạt động của bạn onCreateOptionsMenu (). Nó đặt một nhà máy mặc định sẽ ảnh hưởng đến việc tạo bất kỳ Menu Tùy chọn nào sau đó. Nó không ảnh hưởng đến Tùy chọn Menu đã được tạo (các lần hack trước sử dụng tên hàm là setMenuBackground (), rất dễ gây hiểu lầm vì hàm không đặt bất kỳ thuộc tính menu nào trước khi nó trả về).

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

Cảm ơn bạn đọc và tận hưởng!


15
Điều duy nhất tôi nhận được tin cậy khi cố gắng sử dụng này (và giải pháp tương tự) là 'java.lang.IllegalStateException: Một nhà máy đã được thiết lập trên LayoutInflater` này
Bostone

Làm việc cho tôi! Vì vậy, tuyệt vời để cuối cùng có một giải pháp! Đã thử nghiệm trên Gingerbread, Honeycomb và ICS
Chad Schultz

Đã thử nghiệm trong Samsung Galaxy Nexus (4.1.1) và đang hoạt động! Tốt lắm, Louis!
Felipe Caldas

2
Hoạt động trên Galaxy Nexus 7 (4.1.1) tuy nhiên, màu văn bản được hoàn nguyên cho mỗi lần gọi tiếp theo vào menu sau khi bị ẩn lần đầu tiên.
Will Kru

1
Tôi cũng nhận được IllegalStateException. Có vẻ như bản hack không tương thích với ActionBarSherlock mà tôi đang sử dụng.
Travis

20

Thuộc tính kiểu cho nền menu là android:panelFullBackground.

Mặc dù tài liệu nói gì, nó cần phải là một tài nguyên (ví dụ: @android:color/blackhoặc @drawable/my_drawable), nó sẽ bị hỏng nếu bạn sử dụng trực tiếp giá trị màu.

Điều này cũng sẽ loại bỏ các đường viền mục mà tôi không thể thay đổi hoặc xóa bằng giải pháp của primalpop.

Đối với màu văn bản, tôi không tìm thấy bất kỳ cách nào để thiết lập nó thông qua các kiểu trong 2.2 và tôi chắc chắn rằng tôi đã thử mọi thứ (đó là cách tôi phát hiện ra thuộc tính nền menu). Bạn sẽ cần phải sử dụng giải pháp của primalpop cho điều đó.


3
Tôi phải đặt giá trị này ở đâu? Tôi không thể làm cho nó hoạt động trên Android 2.2. hoặc 2.3
Janusz

1
@Janusz Trong Styles.xml. Điều này có thể sẽ hữu ích: developer.android.com/guide/topics/resources/…
Pilot_51

2
Không làm việc, thoải mái nếu bạn có thể nêu nơi đó là nghĩa vụ để đi, cố gắng ở khắp mọi nơi ngoại trừ làm cho phong cách khác cho các mục menu của tôi để thuộc tính .....
John

14

Đối với Android 2.3, điều này có thể được thực hiện với một số hack rất nặng:

Nguyên nhân gốc rễ của các vấn đề với Android 2.3 là trong LayoutInflater, mConstructorArgs [0] = mContext chỉ được đặt trong khi chạy lệnh gọi tới

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

Tôi đã thử nghiệm để nó hoạt động trên Android 2.3 và vẫn hoạt động trên các phiên bản trước đó. Nếu bất cứ điều gì lại xảy ra trong các phiên bản Android mới hơn, bạn sẽ chỉ thấy kiểu menu mặc định


Mã này chỉ chạy đến phiên bản 2.1 Mã này ở đây có vẻ tốt hơn: stackoverflow.com/questions/2944244/…
Felipe Caldas

Xin chào, tôi đã sử dụng hàm của bạn nhưng tôi gặp lỗi sau đây. Lỗi khi thổi phồng lớp com.android.internal.view.menu.IconMenuItemView và sau đó thêm một ngoại lệ nữa. Lỗi thổi phồng lớp <không xác định> ... bây giờ tôi phải làm gì bây giờ ... ? làm ơn giúp tôi.
Rushabh Patel

13

Cũng vừa gặp phải vấn đề này, trên một Ứng dụng phải tương thích với Gingerbread và vẫn giữ được nhiều kiểu dáng từ các thiết bị hỗ trợ Holo nhất có thể.

Tôi đã tìm thấy một giải pháp tương đối sạch sẽ, phù hợp với tôi.

Trong chủ đề, tôi sử dụng nền có thể vẽ được 9 miếng để có được màu nền tùy chỉnh:

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

Tôi đã từ bỏ việc cố gắng tạo kiểu cho màu văn bản và chỉ sử dụng Spannable để đặt màu văn bản cho mục của mình trong mã:

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

hoạt động tốt cho sự cố của tôi khi sử dụng chủ đề ActionBarSherlock Light trên thiết bị Gingerbread! Với điều này, tôi có thể dễ dàng thay đổi nền cho menu tùy chọn để một ánh sáng màu xám và văn bản màu đen (biểu tượng đã được những người da đen như trong ActionBar Cảm ơn bạn!
florianbaethge

12

Đây là cách tôi giải quyết vấn đề của mình. Tôi chỉ xác định màu nền và màu văn bản trong các kiểu. tức là res> giá trị> tệp styles.xml.

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

1
Format sẽ thay đổi ở khắp mọi nơi
user3156040

nó chỉ thay đổi màu nền của các mục và bố cục tùy chọn menu có phần đệm trên cùng và dưới cùng, điều đó không giúp được gì
FarshidABZ

10

Cần lưu ý một điều là các bạn đang phức tạp hóa vấn đề quá mức như rất nhiều bài viết khác! Tất cả những gì bạn cần làm là tạo các bộ chọn có thể kéo với bất kỳ hình nền nào bạn cần và đặt chúng thành các mục thực tế. Tôi chỉ dành hai giờ để thử các giải pháp của bạn (tất cả đều được đề xuất trên trang này) và không có giải pháp nào hoạt động. Chưa kể rằng có rất nhiều lỗi về cơ bản làm chậm hiệu suất của bạn trong các khối thử / bắt bạn mắc phải.

Dù sao đây cũng là một tệp xml menu:

<?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>

Bây giờ trong item1_selector của bạn:

<?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>

Lần tới khi bạn quyết định đi siêu thị qua Canada, hãy thử google maps!


Tôi hoàn toàn đồng ý. Tại sao phải phát minh lại Android khi anh ấy =) đã tồn tại?
Fredrik

Hoạt động tuyệt vời. Xây dựng một danh sách lớp có thể vẽ được với biểu tượng của bạn và nền mong muốn. Vấn đề duy nhất là tôi không biết liệu tôi có thể thay đổi màu văn bản hay không. Vì vậy không phải mọi màu nền làm việc
Janusz

52
Đẹp, thanh lịch và hoàn toàn không giải quyết được vấn đề.
Aea

1
Nếu tôi không nhầm, điều này chỉ thay đổi nền cho biểu tượng chứ không phải bản thân mục menu, vẫn là màu trắng.
Jrom

3
Đây không phải là câu hỏi. Đây là suy nghĩ hoàn toàn khác.
Kostadin

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

Cái này làm việc tốt cho tôi


làm thế nào để thay đổi màu sắc tiêu đề
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
Vui lòng không làm điều này: name.equalsIgnoreCase ("com.android.internal.view.menu.IconMenuItemView" Như tên đã chỉ rõ, điều này đang sử dụng chi tiết triển khai riêng tư và do đó có thể bị
hỏng

1
IconMenuItemView là lớp tạo và điều khiển menu tùy chọn có nguồn gốc từ lớp View cơ bản. Lớp này là từ mã nguồn android và đã có mặt từ ít nhất là phiên bản api 5. Tôi không thể thấy nó bị phá vỡ trên bất kỳ bản cập nhật nền tảng hoặc thiết bị nào.
Primal Pappachan,

1
Bạn không thể nhìn thấy nó bởi vì bạn không thể nhìn thấy tương lai. Thậm chí nếu có một cách để chắc chắn, đó là thói quen xấu
HXCaine

Cảm ơn, điều này rất hữu ích. Tuy nhiên, không hoạt động trong các trường hợp đặc biệt, chẳng hạn như, các mục được tạo trong onCreateOptionsMenu, nhưng bị tắt trong onPrepareOptionsMenu, nhưng sau đó được bật lại.
HRJ

3

Cảm ơn Marcus! Nó hoạt động trơn tru trên 2.3 bằng cách sửa một số lỗi cú pháp, đây là mã đã sửa

    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
Tất cả tôi nhận được cho việc này: java.lang.IllegalStateException: Một nhà máy đã được thiết lập trên LayoutInflater này
Bostone

để làm cho nó hoạt động với ActionBarSherlock và khung tương thích và tránh IllegalStateException, hãy xem thủ thuật này 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;
        }
    });
}

đây là tệp XML

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

1

Nếu bạn muốn đặt một màu tùy ý, điều này dường như hoạt động khá tốt androidx. Đã thử nghiệm trên KitKat và Pie. Đặt điều này vào 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);
}

Điều này đặt màu android.widget.PopupWindow$PopupBackgroundView, như bạn có thể đoán, vẽ màu nền. Không có khoản thấu chi và bạn cũng có thể sử dụng các màu bán trong suốt.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.