浅色/暗淡触感

时间:2011-11-07 09:07:23

标签: android android-xml

我正在处理的应用程序使用了很多ImageViews作为按钮。这些按钮上的图形使用Alpha通道淡出按钮的边缘,使它们看起来不规则。目前,我们必须为每个按钮生成2个图形(1个用于选定/聚焦/按下状态,另一个用于默认的未选择状态),并使用XML文件中为每个按钮定义的StateListDrawable。

虽然这种方法很好但看起来非常浪费,因为所有选定的图形都只是未选定按钮的着色版本。这些花费时间来制作(无论多少)并占用最终APK中的空间。似乎应该有一种简单的方法来自动完成。

看起来,完美的解决方案是为每个按钮使用ImageViews,并在其tint属性中指定ColorStateList。这种方法的优点是,所有按钮(共享相同色调)只需要一个XML ColorStateList。但它不起作用。如前面提到的here,当提供给tint的值不是单一颜色时,ImageView会抛出NumberFormatException。

我的下一次尝试是为选定的drawable使用LayerDrawable。在图层列表中,我们将原始图像放在堆栈底部,由半透明矩形覆盖。这适用于按钮图形的实体部分。然而,应该是完全透明的边缘现在与顶层的颜色相同。

之前是否有人遇到此问题并找到了合理的解决方案?我想坚持XML方法,但可能会编写一个简单的ImageView子类,它将在代码中进行所需的着色。

8 个答案:

答案 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)

您可以将StateListDrawableLayerDrawable结合使用。

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 也很出色;)

我正在修改一下,它会给你这样的结果:

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

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