我正在处理的应用程序使用了很多ImageViews作为按钮。这些按钮上的图形使用Alpha通道淡出按钮的边缘,使它们看起来不规则。目前,我们必须为每个按钮生成2个图形(1个用于选定/聚焦/按下状态,另一个用于默认的未选择状态),并使用XML文件中为每个按钮定义的StateListDrawable。
虽然这种方法很好但看起来非常浪费,因为所有选定的图形都只是未选定按钮的着色版本。这些花费时间来制作(无论多少)并占用最终APK中的空间。似乎应该有一种简单的方法来自动完成。
看起来,完美的解决方案是为每个按钮使用ImageViews,并在其tint属性中指定ColorStateList。这种方法的优点是,所有按钮(共享相同色调)只需要一个XML ColorStateList。但它不起作用。如前面提到的here,当提供给tint的值不是单一颜色时,ImageView会抛出NumberFormatException。
我的下一次尝试是为选定的drawable使用LayerDrawable。在图层列表中,我们将原始图像放在堆栈底部,由半透明矩形覆盖。这适用于按钮图形的实体部分。然而,应该是完全透明的边缘现在与顶层的颜色相同。
之前是否有人遇到此问题并找到了合理的解决方案?我想坚持XML方法,但可能会编写一个简单的ImageView子类,它将在代码中进行所需的着色。
答案 0 :(得分:17)
对于那些遇到类似需求的人来说,在代码中解决这个问题是相当干净的。这是一个示例:
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);
}
}
它没有完成,但作为概念证明很好。
答案 1 :(得分:6)
Your answer非常出色:)
但是,我只是调用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 :(得分:6)
您可以将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>
答案 3 :(得分:2)
我认为这个解决方案很简单!
步骤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"
答案 4 :(得分:1)
我也有不规则的按钮,需要色调。最后,我只是使用状态颜色列表作为我的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>
答案 5 :(得分:1)
您可以使用我创建的课程https://github.com/THRESHE/TintableImageButton 不完全是最好的解决方案,但它确实有效。
答案 6 :(得分:1)
Your answer 也很出色;)
我正在修改一下,它会给你这样的结果:
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;
}
答案 7 :(得分:0)
我认为这个解决方案很简单:
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;
}
});