Ninepatch отрисовывается с помощью ColorFilter

Я создаю некоторые представления календаря, и я хочу создать фон для LineairLayout, который называется clickabe.

Поэтому я создаю StateListDrawable с двумя изображениями:

  1. Изображение для фона
  2. Изображение, когда элемент был нажат

Пока что работает с этим фрагментом кода:

    NinePatchDrawable background = (NinePatchDrawable) context.getResources().getDrawable(R.drawable.calendar_item);
    Drawable backgroundFocus = context.getResources().getDrawable(R.drawable.calendar_focus);

    int stateFocused = android.R.attr.state_focused;
    int statePressed = android.R.attr.state_pressed;

    StateListDrawable sld = new StateListDrawable();
    sld.addState(new int[]{ stateFocused,  statePressed}, backgroundFocus);
    sld.addState(new int[]{-stateFocused,  statePressed}, backgroundFocus);
    sld.addState(new int[]{-stateFocused}, background);
    return sld;

Но я хотел бы сделать что-то дополнительное. Мне бы хотелось, чтобы пользователь мог передать цвет, который он хочет использовать для отображения фона. Таким образом, фоновая переменная должна быть переменной, но она должна основываться на прорисовке из девяти патчей.

Поэтому я подумал, что могу сделать что-то вроде этого:

background.setColorFilter(Color.RED, PorterDuff.Mode.DST_IN);

Где Color.RED должен быть заменен цветом по выбору пользователя.

Но это не похоже на работу. Девятый патч создан идеально, но без применения цветного фильтра.

Я также пробовал другие PoterDuff.Mode's:

  • SRC
  • SRC_ATOP
  • DST_IN
  • ...

Если у вас есть какие-либо сведения о том, что я делаю неправильно или что я могу сделать, чтобы решить мою проблему, пожалуйста, сообщите мне!:-)

Kr,

кортик

1 ответ

Я не думаю, что вы можете назначить ColorFilters для каждого Drawable в StateListDrawable. Причина: ColorFilter будет удален / заменен, когда StateListDrawable изменит состояние. Чтобы увидеть это в действии, измените порядок операторов так, чтобы:

background.setColorFilter(Color.RED, PorterDuff.Mode.DST_IN);

наступает после создания StateListDrawable. Вы увидите, что ColorFilter применяется. Но, как только состояние меняется (нажмите, затем отпустите), ColorFilter больше не существует.

StateListDrawables позволяют вам установить ColorFilter: StateListDrawable#setColorFilter(ColorFilter), Вот как используется поставляемый (или нулевой) ColorFilter:

StateListDrawable # onStateChange (int []):

@Override
protected boolean onStateChange(int[] stateSet) {
    ....
    if (selectDrawable(idx)) {    // DrawableContainer#selectDrawable(int)
        return true;
    }
    ....
}

DrawableContainer # selectDrawable (int):

public boolean selectDrawable(int idx) {
    ....
    if (idx >= 0 && idx < mDrawableContainerState.mNumChildren) {
        Drawable d = mDrawableContainerState.mDrawables[idx];
        mCurrDrawable = d;
        mCurIndex = idx;

        if (d != null) {
            ....

            // So, at this stage, any ColorFilter you might have supplied
            // to `d` will be replaced by the ColorFilter you
            // supplied to the StateListDrawable, or `null`
            // if you didn't supply any.                 
            d.setColorFilter(mColorFilter);

            ....
        }
    } else {
        ....
    }
}

Обходной путь:

Если это вообще возможно, используйте ImageView (match_parent для измерений) для визуальной коммуникации. Установите StateListDrawable, который вы создали в качестве фона ImageView. Создайте еще один StateListDrawable для наложения:

StateListDrawable sldOverlay = new StateListDrawable();

// Match Colors with states (and ultimately, Drawables)
sldOverlay.addState(new int[] { statePressed }, 
                         new ColorDrawable(Color.TRANSPARENT));

sldOverlay.addState(new int[] { -statePressed }, 
                         new ColorDrawable(Color.parseColor("#50000000")));

// Drawable that you already have
iv1.setBackground(sld);

// Drawable that you just created
iv1.setImageDrawable(sldOverlay);

Другая возможность: использовать FrameLayout вместо LinearLayout. LinearLayouts не имеет свойства переднего плана.

// StateListDrawable
frameLayout.setBackground(sld);

// For tint
frameLayout.setForeground(sldOverlay);

Это включает в себя перерасход, что делает его неоптимальным решением / обходным путем. Возможно, вы можете взглянуть на расширение StateListDrawable и DrawableContainer. И поскольку вы не используете ColorFilter для StateListDrawable, вы можете удалить d.setColorFilter(mColorFilter); от переопределенного DrawableContainer#selectDrawable(int),

Другие вопросы по тегам