2014-10-11 66 views
0

我有一個彈出式的主題,正確地設置背景顏色,但不會改變文本的顏色。我怎麼做?如何給Android中的選項菜單添加顏色?

<style name="PopupTheme" parent="@style/Widget.AppCompat.Light.ListPopupWindow"> 
    <item name="background">@color/green</item> 
    <item name="android:popupBackground">@color/green</item> 

    <!-- These are not being applied --> 
    <item name="actionMenuTextColor">@color/white</item> 
    <item name="android:actionMenuTextColor">@color/white</item> 
    <item name="android:textColor">@color/white</item> 
    <item name="android:foreground">@color/white</item> 
    <item name="android:colorForeground">@color/white</item> 
</style> 

回答

0

這顯然是一個很多程序員都有的問題,谷歌尚未提供令人滿意的支持解決方案。

有很多交叉的意圖和誤解左右浮動的帖子關於這個主題的,所以請回答前仔細閱讀這整個的答案。

下面我包括來自其他答案此頁面上的黑客更「精」和良好註釋的版本,也是從這些合併的想法非常密切相關的問題:

Change background color of android menu

How to change the background color of the options menu?

Android: customize application's menu (e.g background color)

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

Android MenuItem Toggle Button

Is it possible to make the Android options menu background non-translucent?

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

Setting the menu background to be opaque

我測試上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。所以我們必須使用能夠在當前版本中運行的解決方案,並且旨在最大限度地減少未來版本崩潰/崩潰的可能性。我們想要一個解決方案,如果它必須失敗,纔會優雅地恢復到默認行爲。

有很多合法的原因,可能需要控制選項菜單的外觀(通常爲應用程序的其餘部分匹配視覺樣式),所以我不會詳談。

有一個谷歌Android漏洞發佈了關於這一點,請通過主演這個bug添加你的支持(注意谷歌不鼓勵「我也是」的評論:只是一個明星就夠了):將

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

摘要目前的解決方案:

幾個海報建議涉及LayoutInflater.Factory的破解。建議的黑客適用於Android < = 2.2並且對於Android 2.3失敗,因爲黑客做出了一個無法證明的假設:可以直接調用LayoutInflater.getView(),而不需要在同一個LayoutInflater實例中調用LayoutInflater.inflate()。 Android 2.3中的新代碼打破了這個假設,導致了NullPointerException。

下面我稍微精緻的黑客並不依賴於這個假設。此外,黑客還依賴於使用內部的,未公開的類名「com.android.internal.view.menu.IconMenuItemView」作爲字符串(而不是Java類型)。我看不出有什麼辦法可以避免這種情況,並且仍然達到了既定的目標。但是,如果「com.android.internal.view.menu.IconMenuItemView」沒有出現在當前系統上,那麼可以小心翼翼地進行破解。

再一次,明白這是一個黑客,絕不是我聲稱這將適用於所有平臺。但是我們的開發人員並不是生活在一個夢幻般的學術世界裏,書中的一切都必須依靠:我們有一個需要解決的問題,我們必須盡我們所能解決問題。例如,「3. com.android.internal.view.menu.IconMenuItemView」似乎不太可能存在於3.X平板電腦上,因爲它們使用操作欄而不是選項菜單。最後,一些開發者通過完全禁止Android選項菜單並編寫他們自己的菜單類(參見上面的一些鏈接)解決了這個問題。我還沒有嘗試過,但如果你有時間寫自己的視圖並找出如何取代Android的視圖(我敢肯定魔鬼在這裏的細節),那麼它可能是一個很好的解決方案,不需要任何無證黑客。

HACK:

這是代碼。

要使用此代碼,請從您的活動onCreate()或您的活動onCreateOptionsMenu()中調用addOptionsMenuHackerInflaterFactory()ONCE。它設置了一個默認的工廠,將影響後續創建任何選項菜單。它不會影響已經創建的選項菜單(以前的黑客使用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; 
     } 
    }); 
} 

感謝您的閱讀和享受!

+0

我已經閱讀過這篇文章,因爲你在任何地方都會發布它,但它不能解決問題。我找到了答案(但沒有任何意義)。如果我在應用程序主題中設置了' @ style/PopupTheme',然後在該主題內設置'textColor',它就可以工作。 – 2014-10-11 06:55:03

相關問題