Đâ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!