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

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

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

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

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

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

Для этого вы можете комбинировать 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> 

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

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

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

Ваш ответ был отличный 🙂

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

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

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

Шаг 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" 

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

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

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

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

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

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

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