Подтвердить что ты не робот

Оттенок/тусклый на ощупь

В приложении, над которым я сейчас работаю, много кнопок ImageViews используются в качестве кнопок. Графика на этих кнопках использует альфа-канал, чтобы погасить края кнопки и заставить их выглядеть нерегулярными. В настоящее время мы должны создавать по 2 графика для каждой кнопки (1 для выбранного/сфокусированного/нажатого состояния, а другое для невыбранного состояния по умолчанию) и использовать для каждой кнопки StateListDrawable, определенные в файле XML.

В то время как это работает отлично, это кажется чрезвычайно расточительным, поскольку все выбранные графики - это просто тонированные версии невыделенных кнопок. Это требует времени, чтобы произвести (хотя бы немного) и заняло место в финальной APK. Кажется, что для этого должен быть простой способ.

Идеальное решение, по-видимому, состоит в том, чтобы использовать ImageViews для каждой кнопки и указать в своем атрибуте tint ColorStateList. Преимущество такого подхода состоит в том, что для всех кнопок (которые имеют один и тот же оттенок) необходим только один XML ColorStateList. Однако это не сработает. Как упоминалось здесь, ImageView генерирует исключение NumberFormatException, когда значение, заданное для оттенка, представляет собой нечто иное, чем один цвет.

Моя следующая попытка состояла в том, чтобы использовать LayerDrawable для выбранного drawable. Внутри списка слоев у нас будет исходное изображение в нижней части стека, покрытого полупрозрачным прямоугольником. Это работало над прочными частями графической кнопки. Однако края, которые должны были быть полностью прозрачными, были теперь того же цвета, что и верхний слой.

Кто-нибудь столкнулся с этой проблемой раньше и нашел разумное решение? Я хотел бы придерживаться XML-подходов, но, вероятно, будет кодировать простой подкласс ImageView, который будет делать требуемый оттенок в коде.

4b9b3361

Ответ 1

Вы можете комбинировать StateListDrawable и LayerDrawable для этого.

public Drawable getDimmedDrawable(Drawable drawable) {
        Resources resources = getContext().getResources();
        StateListDrawable stateListDrawable = new StateListDrawable();
        LayerDrawable layerDrawable = new LayerDrawable(new Drawable[]{
                drawable,
                new ColorDrawable(resources.getColor(R.color.translucent_black))
        });
        stateListDrawable.addState(new int[]{android.R.attr.state_pressed}, layerDrawable);
        stateListDrawable.addState(new int[]{android.R.attr.state_focused}, layerDrawable);
        stateListDrawable.addState(new int[]{android.R.attr.state_selected}, layerDrawable);
        stateListDrawable.addState(new int[]{}, drawable);
        return stateListDrawable;
}

Я предполагаю, что наш colors.xml выглядит как

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="translucent_black">#80000000</color>
</resources>

Ответ 2

Для тех, кто столкнулся с подобной потребностью, решение этого в коде довольно чисто. Вот пример:

public class TintableButton extends ImageView {

    private boolean mIsSelected;

    public TintableButton(Context context) {
        super(context);
        init();
    }   

    public TintableButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }   

    public TintableButton(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    private void init() {
        mIsSelected = false;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN && !mIsSelected) {
            setColorFilter(0x99000000);
            mIsSelected = true;
        } else if (event.getAction() == MotionEvent.ACTION_UP && mIsSelected) {
            setColorFilter(Color.TRANSPARENT);
            mIsSelected = false;
        }

        return super.onTouchEvent(event);
    }
}

Это не завершено, но хорошо работает как доказательство концепции.

Ответ 3

Ваш ответ был превосходным:)

Однако вместо создания объекта кнопки я вызываю только OnTouchlistener для изменения состояния каждого представления.

public boolean onTouch(View v, MotionEvent event) {
    if (event.getAction() == MotionEvent.ACTION_DOWN) {
        Drawable background =  v.getBackground();
        background.setColorFilter(0xBB000000, PorterDuff.Mode.SCREEN);
        v.setBackgroundDrawable(background);
    } else if (event.getAction() == MotionEvent.ACTION_UP) {
        Drawable background =  v.getBackground();
        background.setColorFilter(null);
        v.setBackgroundDrawable(background);
    }

    return true;

}

Получает те же результаты, хотя

Ответ 4

Я думаю, что это решение просто!

Шаг 1: Создайте res/drawable/dim_image_view.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_focused="true" android:drawable="@drawable/dim_image_view_normal"/> <!-- focused -->
    <item android:state_pressed="true" android:drawable="@drawable/dim_image_view_pressed"/> <!-- pressed -->
    <item android:drawable="@drawable/dim_image_view_normal"/> <!--default -->
</selector>

Шаг 2: Создайте res/drawable/dim_image_view_normal.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item><bitmap android:src="@mipmap/your_icon"/></item>
</layer-list>

Шаг 3: Создайте res/drawable/dim_image_view_pressed.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
    <item><bitmap android:src="@mipmap/your_icon" android:alpha="0.5"></bitmap></item>
</layer-list>

Шаг 4: Попробуйте установить изображение в формате xml как:

android:src="@drawable/dim_image_view"

Ответ 5

У меня тоже есть нерегулярные кнопки и нужен оттенок. В конце концов, я просто прибегал к тому, чтобы иметь статус colorlist как мой фон ImageButton. Создается впечатление, что цвет кнопки меняется при нажатии и очень прост и менее интенсивный. Я знаю, что это не совсем оттенок, но он дает ему, дает необходимую визуальную обратную связь пользователю в том, что обычно является мимолетным моментом.

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
    <item
        android:state_pressed="true"
        android:drawable="@android:color/darker_gray" />
    <item android:drawable="@android:color/transparent" />
</selector>

Ответ 7

Ваш ответ также был превосходным;)

Я немного модифицирую, он даст вам результат следующим образом:

button_normalbutton_pressed

Rect rect;
Drawable background;
boolean hasTouchEventCompleted = false;

@Override
public boolean onTouch(View v, MotionEvent event) {
    if (event.getAction() == MotionEvent.ACTION_DOWN) {
        rect = new Rect(v.getLeft(), v.getTop(), v.getRight(),
                v.getBottom());
        hasTouchEventCompleted = false;
        background = v.getBackground();
        background.setColorFilter(0x99c7c7c7,
                android.graphics.PorterDuff.Mode.MULTIPLY);
        v.setBackgroundDrawable(background);
    } else if (event.getAction() == MotionEvent.ACTION_UP
            && !hasTouchEventCompleted) {
        background.setColorFilter(null);
        v.setBackgroundDrawable(background);

    } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
        if (!rect.contains(v.getLeft() + (int) event.getX(), v.getTop()
                + (int) event.getY())) {
            // User moved outside bounds
            background.setColorFilter(null);
            v.setBackgroundDrawable(background);
            hasTouchEventCompleted = true;
        }
    }


    //Must return false, otherwise you need to handle Click events yourself.  
    return false;
}

Ответ 8

Я думаю, что это решение достаточно просто:

    final Drawable drawable = ... ;
    final int darkenValue = 0x3C000000;
    mButton.setOnTouchListener(new OnTouchListener() {
        Rect rect;
        boolean hasColorFilter = false;

        @Override
        public boolean onTouch(final View v, final MotionEvent motionEvent) {
            switch (motionEvent.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    rect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
                    drawable.setColorFilter(darkenValue, Mode.DARKEN);
                    hasColorFilter = true;
                    break;
                case MotionEvent.ACTION_MOVE:
                    if (rect.contains(v.getLeft() + (int) motionEvent.getX(), v.getTop()
                            + (int) motionEvent.getY())) {
                        if (!hasColorFilter)
                            drawable.setColorFilter(darkenValue, Mode.DARKEN);
                        hasColorFilter = true;
                    } else {
                        drawable.setColorFilter(null);
                        hasColorFilter = false;
                    }
                    break;
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:
                    drawable.setColorFilter(null);
                    hasColorFilter = false;
                    break;
            }
            return false;
        }
    });