AppCompat
メニュー項目にToolbar
ライブラリのドロアブルを使用すると、色付けは期待どおりに機能します。このような:
<item
Android:id="@+id/action_clear"
Android:icon="@drawable/abc_ic_clear_mtrl_alpha" <-- from AppCompat
Android:title="@string/clear" />
ただし、独自のドロウアブルを使用する場合、または実際にドローアブルをAppCompat
ライブラリから自分のプロジェクトにコピーする場合でも、まったく色付けされません。
<item
Android:id="@+id/action_clear"
Android:icon="@drawable/abc_ic_clear_mtrl_alpha_copy" <-- copy from AppCompat
Android:title="@string/clear" />
AppCompat
Toolbar
には、そのライブラリのドロアブルのみを着色する特別な魔法がありますか?これを自分のドロアブルで動作させる方法はありますか?
compileSdkVersion = 21
およびtargetSdkVersion = 21
を使用してAPIレベル19デバイスでこれを実行し、AppCompat
のすべてを使用する
abc_ic_clear_mtrl_alpha_copy
は、AppCompat
からのabc_ic_clear_mtrl_alpha
pngの正確なコピーです。
編集:
色合いは、テーマのAndroid:textColorPrimary
に設定した値に基づいています。
例えば。 <item name="Android:textColorPrimary">#00FF00</item>
は緑の色合いを与えます。
スクリーンショット
AppCompatのドロウアブルで期待通りの色合い
AppCompatからコピーされたドロアブルで色合いが機能しない
AppCompatのTintManagerのソースコードを見ると、以下が表示されるためです。
/**
* Drawables which should be tinted with the value of {@code R.attr.colorControlNormal},
* using the default mode.
*/
private static final int[] TINT_COLOR_CONTROL_NORMAL = {
R.drawable.abc_ic_ab_back_mtrl_am_alpha,
R.drawable.abc_ic_go_search_api_mtrl_alpha,
R.drawable.abc_ic_search_api_mtrl_alpha,
R.drawable.abc_ic_commit_search_api_mtrl_alpha,
R.drawable.abc_ic_clear_mtrl_alpha,
R.drawable.abc_ic_menu_share_mtrl_alpha,
R.drawable.abc_ic_menu_copy_mtrl_am_alpha,
R.drawable.abc_ic_menu_cut_mtrl_alpha,
R.drawable.abc_ic_menu_selectall_mtrl_alpha,
R.drawable.abc_ic_menu_paste_mtrl_am_alpha,
R.drawable.abc_ic_menu_moreoverflow_mtrl_alpha,
R.drawable.abc_ic_voice_search_api_mtrl_alpha,
R.drawable.abc_textfield_search_default_mtrl_alpha,
R.drawable.abc_textfield_default_mtrl_alpha
};
/**
* Drawables which should be tinted with the value of {@code R.attr.colorControlActivated},
* using the default mode.
*/
private static final int[] TINT_COLOR_CONTROL_ACTIVATED = {
R.drawable.abc_textfield_activated_mtrl_alpha,
R.drawable.abc_textfield_search_activated_mtrl_alpha,
R.drawable.abc_cab_background_top_mtrl_alpha
};
/**
* Drawables which should be tinted with the value of {@code Android.R.attr.colorBackground},
* using the {@link Android.graphics.PorterDuff.Mode#MULTIPLY} mode.
*/
private static final int[] TINT_COLOR_BACKGROUND_MULTIPLY = {
R.drawable.abc_popup_background_mtrl_mult,
R.drawable.abc_cab_background_internal_bg,
R.drawable.abc_menu_hardkey_panel_mtrl_mult
};
/**
* Drawables which should be tinted using a state list containing values of
* {@code R.attr.colorControlNormal} and {@code R.attr.colorControlActivated}
*/
private static final int[] TINT_COLOR_CONTROL_STATE_LIST = {
R.drawable.abc_edit_text_material,
R.drawable.abc_tab_indicator_material,
R.drawable.abc_textfield_search_material,
R.drawable.abc_spinner_mtrl_am_alpha,
R.drawable.abc_btn_check_material,
R.drawable.abc_btn_radio_material
};
/**
* Drawables which contain other drawables which should be tinted. The child drawable IDs
* should be defined in one of the arrays above.
*/
private static final int[] CONTAINERS_WITH_TINT_CHILDREN = {
R.drawable.abc_cab_background_top_material
};
これは、着色するためにホワイトリストに登録された特定のresourceIdを持っていることを意味します。
しかし、これらの画像がどのように色付けされているかをいつでも見ることができ、同じことができると思います。ドロアブルにColorFilterを設定するのと同じくらい簡単です。
新しいサポートライブラリv22.1の後、次のようなものを使用できます。
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_home, menu);
Drawable drawable = menu.findItem(R.id.action_clear).getIcon();
drawable = DrawableCompat.wrap(drawable);
DrawableCompat.setTint(drawable, ContextCompat.getColor(this,R.color.textColorPrimary));
menu.findItem(R.id.action_clear).setIcon(drawable);
return true;
}
ColorFilter
にMenuItem
(色合い)を設定するのは簡単です。以下に例を示します。
Drawable drawable = menuItem.getIcon();
if (drawable != null) {
// If we don't mutate the drawable, then all drawable's with this id will have a color
// filter applied to it.
drawable.mutate();
drawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
drawable.setAlpha(alpha);
}
上記のコードは、さまざまなテーマをサポートしたい場合で、色や透明度のためだけに余分なコピーを持ちたくない場合に非常に役立ちます。
ここをクリックオーバーフローを含むメニュー内のすべてのドロアブルにColorFilter
を設定するヘルパークラスの場合アイコン。
onCreateOptionsMenu(Menu menu)
では、メニューと出来上がりを膨らませた後にMenuColorizer.colorMenu(this, menu, color);
を呼び出すだけです。アイコンに色が付いています。
私はこれからこのアプローチを好みました link
以下を使用してXMLレイアウトを作成します。
<?xml version="1.0" encoding="utf-8"?>
<bitmap
xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:src="@drawable/ic_action_something"
Android:tint="@color/color_action_icons_tint"/>
メニューからこのドロウアブルを参照します:
<item
Android:id="@+id/option_menu_item_something"
Android:icon="@drawable/ic_action_something_tined"
app:iconTint
属性は、サポートライブラリのSupportMenuInflater
に実装されています(少なくとも28.0.0では)。
API 15以降で正常にテストされました。
メニューリソースファイル:
<menu
xmlns:Android="http://schemas.Android.com/apk/res/Android"
xmlns:app="http://schemas.Android.com/apk/res-auto">
<item
Android:id="@+id/menu_settings"
Android:icon="@drawable/ic_settings_white_24dp"
app:iconTint="?attr/appIconColorEnabled" <!-- using app name space instead of Android -->
Android:menuCategory="system"
Android:orderInCategory="1"
Android:title="@string/menu_settings"
app:showAsAction="never"
/>
<item
Android:id="@+id/menu_themes"
Android:icon="@drawable/ic_palette_white_24dp"
app:iconTint="?attr/appIconColorEnabled"
Android:menuCategory="system"
Android:orderInCategory="2"
Android:title="@string/menu_themes"
app:showAsAction="never"
/>
<item
Android:id="@+id/action_help"
Android:icon="@drawable/ic_help_white_24dp"
app:iconTint="?attr/appIconColorEnabled"
Android:menuCategory="system"
Android:orderInCategory="3"
Android:title="@string/menu_help"
app:showAsAction="never"
/>
</menu>
(この場合、?attr/appIconColorEnabled
はアプリのテーマのカスタムカラー属性で、アイコンリソースはベクトル描画可能です。)
このスレッドのほとんどのソリューションは、新しいAPIを使用するか、リフレクションを使用するか、集中的なビュールックアップを使用して、膨張したMenuItem
を取得します。
ただし、それを行うためのよりエレガントなアプローチがあります。 「カスタムティントの適用」ユースケースがパブリックスタイリング/テーマAPIでうまく機能しないため、カスタムツールバーが必要です。
public class MyToolbar extends Toolbar {
... some constructors, extracting mAccentColor from AttrSet, etc
@Override
public void inflateMenu(@MenuRes int resId) {
super.inflateMenu(resId);
Menu menu = getMenu();
for (int i = 0; i < menu.size(); i++) {
MenuItem item = menu.getItem(i);
Drawable icon = item.getIcon();
if (icon != null) {
item.setIcon(applyTint(icon));
}
}
}
void applyTint(Drawable icon){
icon.setColorFilter(
new PorterDuffColorFilter(mAccentColor, PorterDuff.Mode.SRC_IN)
);
}
}
必ずアクティビティ/フラグメントコードを呼び出してください:
toolbar.inflateMenu(R.menu.some_menu);
toolbar.setOnMenuItemClickListener(someListener);
リフレクションもビュー検索も、コードもそれほど多くないでしょう?
そして今、あなたはとんでもないonCreateOptionsMenu/onOptionsItemSelected
を無視することができます。
私が使用するソリューションは次のとおりです。 onPrepareOptionsMenu()または同等の場所の後に呼び出すことができます。 mutate()の理由は、複数の場所でアイコンを使用する場合です。変異がなければ、彼らはすべて同じ色合いを帯びます。
public class MenuTintUtils {
public static void tintAllIcons(Menu menu, final int color) {
for (int i = 0; i < menu.size(); ++i) {
final MenuItem item = menu.getItem(i);
tintMenuItemIcon(color, item);
tintShareIconIfPresent(color, item);
}
}
private static void tintMenuItemIcon(int color, MenuItem item) {
final Drawable drawable = item.getIcon();
if (drawable != null) {
final Drawable wrapped = DrawableCompat.wrap(drawable);
drawable.mutate();
DrawableCompat.setTint(wrapped, color);
item.setIcon(drawable);
}
}
private static void tintShareIconIfPresent(int color, MenuItem item) {
if (item.getActionView() != null) {
final View actionView = item.getActionView();
final View expandActivitiesButton = actionView.findViewById(R.id.expand_activities_button);
if (expandActivitiesButton != null) {
final ImageView image = (ImageView) expandActivitiesButton.findViewById(R.id.image);
if (image != null) {
final Drawable drawable = image.getDrawable();
final Drawable wrapped = DrawableCompat.wrap(drawable);
drawable.mutate();
DrawableCompat.setTint(wrapped, color);
image.setImageDrawable(drawable);
}
}
}
}
}
これはオーバーフローを処理しませんが、そのためにこれを行うことができます:
レイアウト:
<Android.support.v7.widget.Toolbar
...
Android:theme="@style/myToolbarTheme" />
スタイル:
<style name="myToolbarTheme">
<item name="colorControlNormal">#FF0000</item>
</style>
これは、appcompat v23.1.0以降で機能します。