Оттенок / тусклый рисунок на ощупь - PullRequest
16 голосов
/ 07 ноября 2011

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

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

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

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

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

Ответы [ 8 ]

17 голосов
/ 09 ноября 2011

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

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);
    }
}

Он не закончен, но работает как подтверждение концепции.

6 голосов
/ 18 марта 2013

Вы можете комбинировать 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>
6 голосов
/ 14 декабря 2011

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

Однако вместо создания объекта кнопки я просто вызываю 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;

}

Дает такие же результаты, хотя

2 голосов
/ 17 декабря 2015

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

Шаг 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"
1 голос
/ 26 сентября 2014

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

Я немного изменяю, это даст вам такой результат:

button_normal button_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;
}
1 голос
/ 24 февраля 2012

Вы можете использовать созданный мной класс https://github.com/THRESHE/TintableImageButton Не совсем лучшее решение, но оно работает.

1 голос
/ 14 декабря 2011

У меня тоже кнопки выглядят нерегулярно, и мне нужен оттенок. В конце концов, я просто использовал цветовой список состояний в качестве фона 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>
0 голосов
/ 21 сентября 2015

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

    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;
        }
    });
...