隠しリソースを使う

アクションバーに「リフレッシュ」ボタンをつけようとして、次のようなメニューリソースを書いた。

<item
  android:id="@+id/action_refresh"
  android:icon="@android:drawable/ic_menu_refresh"
  android:showAsAction="always"
  android:title="@string/action_refresh"/>

すると "error: Error: Resource is not public. (at 'icon' with value '@android:drawable/ic_menu_refresh')." というエラーがでてリソースをコンパイルできない。
SDK の res/drawable-hdpi/ などの下に画像はあるけれど、どうやら OS のバージョンなどによって存在したりしなかったり、というリソースは private 指定されているのだとか。

ということで先のリソース定義から android:icon="..." 行を削除して、オプションメニューを生成時に動的にアイコンを設定するように変更。

@Override
public boolean onCreateOptionsMenu(Menu menu) {
  getMenuInflater().inflate(R.menu.main, menu);
  final String idString = "@*android:drawable/ic_menu_refresh";
  final int id = getResources().getIdentifier(idString, null, null);
  menu.findItem(R.id.action_refresh).setIcon(id);
  return true;
}

非公開のリソースは @android でなく @*android とすれば参照できるのだとか。
Resources#getIdentifier() で非公開リソースの ID を探し、これを MenuItem#setIcon() で設定している。

非公開リソースが見つからなかった場合は文字列 @string/action_refresh が表示される。
というのは Resources#getIdentifier() は見つからなければ 0 を返す。 MenuItem#setIcon() に 0 を渡すとアイコン設定を削除する(ドキュメントには書かれていないので、動かして確かめた限りだけれど)。よって指定した文字列が表示される。

昔の SDK ではリソース定義に @*android を指定できたのかもしれないけれど、 SDK 22.0.5 では "Illegal resource reference: @*android resources are private and not always present" とエラーがでてコンパイルできない。よってコードで対応するしかなさそう。