Tint / dim drawable on touch

2019-03-08 20:25发布

The app I'm currently working on uses a lot of ImageViews as buttons. The graphics on these buttons use the alpha channel to fade out the edges of the button and make them look irregular. Currently, we have to generate 2 graphics for each button (1 for the selected/focused/pressed state and the other for the default unselected state) and use a StateListDrawable defined in an XML file for each button.

While this works fine it seems extremely wasteful since all of the selected graphics are simply tinted versions of the unselected buttons. These take time to produce (however little) and take up space in the final APK. It seems like there should be an easy way to this automatically.

The perfect solution, it would seem, is to use ImageViews for each button and specify in its tint attribute a ColorStateList. This approach has the advantage that only a single XML ColorStateList is needed for all of the buttons (that share the same tint). However it does not work. As mentioned here, ImageView throws a NumberFormatException when the value provided to tint is anything other than a single color.

My next attempt was to use a LayerDrawable for the selected drawable. Inside the layer list, we would have the original image at the bottom of the stack covered by a semi-transparent rectangle. This worked on the solid parts of the button graphic. However the edges which were supposed to be entirely transparent, were now the same color as the top layer.

Has anyone encountered this issue before and found a reasonable solution? I would like to stick to XML approaches but will probably code up a simple ImageView subclass that will do the required tinting in code.

8条回答
冷血范
2楼-- · 2019-03-08 21:30

Your answer was excellent :)

However instead of creating a button object, I am only calling an OnTouchlistener to change the state of every view.

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;

}

Gives the same results though

查看更多
时光不老,我们不散
3楼-- · 2019-03-08 21:30

I think this solution is simple!

Step 1: Create 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>

Step 2: Create 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>

Step 3: Create 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>

Step 4: Try setting the image in xml layout as:

android:src="@drawable/dim_image_view"
查看更多
登录 后发表回答