AppCompat 툴바의 MenuItem 색조


93

메뉴 항목에 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해당 라이브러리의 유일한 색조 드로어 블 에 특별한 마법이 있습니까? 내 드로어 블과 함께 작동하도록하는 방법은 없나요?

와 API 레벨 19 디바이스에 실행 compileSdkVersion = 21하고 targetSdkVersion = 21, 또한 모든 것을 사용하여AppCompat

abc_ic_clear_mtrl_alpha_copyabc_ic_clear_mtrl_alphaPNG 의 정확한 사본입니다.AppCompat

편집하다:

색조는 android:textColorPrimary내 테마에서 설정 한 값을 기반으로합니다 .

예를 들어 <item name="android:textColorPrimary">#00FF00</item>나에게 녹색 색조를 줄 것입니다.

스크린 샷

AppCompat에서 드로어 블로 예상대로 작동하는 색조 AppCompat에서 드로어 블로 예상대로 작동하는 색조

AppCompat에서 복사 한 드로어 블에서 색조가 작동하지 않음 AppCompat에서 복사 한 드로어 블에서 색조가 작동하지 않음


두 스타일 모두 동일한 부모가 있습니까? 자신의 스타일로 탑 스타일을 확장하면 어떨까요?
G_V

스타일에는 차이가 없습니다. 유일한 차이점은 .png 파일 인 드로어 블입니다
greve

드로어 블은 코드에서 원래 AppCombat 드로어 블의 정확한 사본처럼 보입니다.
G_V

내가 복사 한 png 파일입니다. 그들은 똑같습니다.
greve 2014

그렇다면 동일한 스타일과 동일한 이미지를 가진 코드가 원본과 정확히 어떻게 다른가요?
G_V 2014

답변:


31

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를 설정하는 것만 큼 쉽습니다.


어, 그게 제가 두려워했던 것입니다. SDK에서 AppCompat의 소스 코드를 찾지 못했기 때문에이 부분을 직접 찾지 못했습니다. 그럼 googlesource.com에서 찾아봐야 할 것 같아요. 감사!
greve 2014

8
접선적인 질문이라는 것을 알고 있지만 왜 화이트리스트가 있습니까? 이 아이콘으로 틴팅을 할 수 있다면 왜 우리 자신의 아이콘에 틴팅을 할 수 없습니까? 또한 가장 중요한 것 중 하나 인 작업 표시 줄 아이콘 (사용자 지정 색상 포함)을 생략 할 때 거의 모든 것을 역 호환 (AppCompat 사용)으로 만드는 것이 중요한 점입니다.
Zsolt Safrany 2015 년

1
Google의 문제 추적기에 수정 된 것으로 표시된 문제가 있지만 저에게는 작동하지 않지만 여기에서 추적 할 수 있습니다. issuetracker.google.com/issues/37127128
niknetniko

그들은 이것이 고정되었다고 주장하지만 그렇지 않습니다. 어머, 나는 Android 테마 엔진, AppCompat 및 이와 관련된 모든 쓰레기를 싫어합니다. "Github repo 브라우저"샘플 앱에서만 작동합니다.
Martin Marconcini

97

새로운 지원 라이브러리 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;
    }

1
이 경우에는 낡은 setColorFilter()것이 훨씬 바람직 하다고 말하고 싶습니다 .
natario 2015

@mvai, 왜 setColorFilter ()가 더 선호됩니까?
wilddev 2015 년

4
@wilddev 간결함. menu.findItem (). getIcon (). setColorFilter () 갈 수 있는데 왜 지원 DrawableCompat 클래스를 귀찮게합니까? 하나의 라이너와 클리어.
natario 2014 년

4
한 줄짜리 인수는 전체 논리를 자신의 TintingUtils.tintMenuIcon (...) 메서드 또는 호출하려는 모든 것으로 추상화 할 때 관련이 없습니다. 나중에 로직을 변경하거나 조정해야하는 경우 애플리케이션 전체가 아닌 한곳에서 수행합니다.
Dan Dar3 2015-09-27

1
굉장합니다!
shariful 이슬람

82

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);메뉴를 늘리면 팽창 후; 아이콘이 착색됩니다.


고마워요, 확실히 해보겠습니다!
greve

3
drawable.mutate ()에 대해 고마워 해 주셔서 감사합니다. 내 모든 아이콘이 왜 착색되는지 알아 내려고 책상에 머리를 대고있었습니다!
Scott Cooper

49

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앱 테마의 사용자 지정 색상 속성이었고 아이콘 리소스는 벡터 드로어 블이었습니다.)


5
이것이 새로운 대답이어야합니다! 또한, 메모를 기쁘게 android:iconTint하고 android:iconTintMode일을하지 않는,하지만 함께 접두어 app:대신 android:마법처럼 작품 (내 자신의 벡터 드로어 블에, API> = 21)
Sebastiaan 알바 레즈 로드리게스

프로그래밍 방식으로 호출하는 경우 : SupportMenuInflater메뉴가 SupportMenu유사 하지 않은 경우 사용자 지정 논리를 적용하지 않는 경우 MenuBuilder일반으로 돌아갑니다 MenuInflater.
geekley

이 경우 use AppCompatActivity.startSupportActionMode(callback)및 적절한 지원 구현 androidx.appcompat이 콜백으로 전달됩니다.
geekley

30

개인적으로이 링크 에서이 접근 방식을 선호했습니다.

다음을 사용하여 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"

2
이 링크가 질문에 답할 수 있지만 여기에 답변의 필수 부분을 포함하고 참조 용 링크를 제공하는 것이 좋습니다. 링크 된 페이지가 변경되면 링크 전용 답변이 무효화 될 수 있습니다.
tomloprod

귀하의 의견에 감사드립니다 나는 질문을 편집했습니다. @tomloprod
N 제이

4
이것이 제가 선호하는 솔루션입니다. 그러나 현재로서는 새로운 벡터 드로어 블 유형을 소스로 사용하는 경우이 솔루션이 작동하지 않는 것 같습니다.
Michael De Soto

1
@haagmm이 솔루션은 API> = 21이 필요합니다. 또한 벡터에서도 작동합니다.
신경 전달 물질

1
벡터에서는 작동하지 않아야하며 루트 태그는 bitmap입니다. 벡터를 색칠하는 다른 방법이 있습니다. 아마도 누군가가 ... 너무 여기 착색 벡터를 추가 할 수 있습니다
milosmns

11

이 스레드의 대부분의 솔루션은 최신 API를 사용하거나 리플렉션을 사용하거나 집중 뷰 조회를 사용하여 inflated 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)
        );
    }

}

Activity / Fragment 코드를 호출했는지 확인하십시오.

toolbar.inflateMenu(R.menu.some_menu);
toolbar.setOnMenuItemClickListener(someListener);

반사도없고, 뷰 조회도없고, 코드도 많지 않습니다.

그리고 이제 당신은 어리석은 onCreateOptionsMenu/onOptionsItemSelected.


기술적으로보기 조회를 수행하고 있습니다. 뷰를 반복하고 null이 아닌지 확인합니다. )
마틴 Marconcini

당신은 확실히 옳습니다 :-) 그럼에도 불구하고, Menu#getItem()항목은 ArrayList에 저장되기 때문에 도구 모음에서 복잡성은 O (1)입니다. View#findViewById순회 ( 내 대답에서 뷰 조회 라고 함 )와는 다르며 복잡성은 일정하지 않습니다. :-)
Drew

사실 저는 매우 비슷한 일을했습니다. Android가이 모든 것을 수년 동안 간소화하지 못했다는 사실에 여전히 충격적입니다…
Martin Marconcini

이 방법으로 오버플로 아이콘과 햄버거 아이콘의 색상을 어떻게 변경할 수 있습니까?
산드라

8

여기 내가 사용하는 해결책이 있습니다. 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에서 작동합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.