Search code examples
androidmaterial-designnine-patchandroid-bitmap

Can't apply a colorFilter to text selection handles


I'm trying to bring material text selection handles to my app. I got drawables from the SDK for middle/right/left handle (bitmaps) and text cursor (9-patch), and set:

<item name="android:textSelectHandleLeft">@drawable/text_select_handle_left_mtrl_alpha</item>
<item name="android:textSelectHandleRight">@drawable/text_select_handle_right_mtrl_alpha</item>
<item name="android:textSelectHandle">@drawable/text_select_handle_middle_mtrl_alpha</item>
<item name="android:textCursorDrawable">@drawable/text_cursor_mtrl_alpha</item>

It works as expected. However, in Lollipop these drawables are tinted with a particular color in XML using the android:tint attribute, which I can't use on API<21. So I'm trying to set a color filter at runtime.

enter image description here

  • Text cursor does not get tinted. I think this might be due to it being a 9 patch. How can a 9-patch drawable be filtered at runtime? I tried probably all of PorterDuff.Modes.

  • Right/left handles are black, while middle handle is white.

I.e., non of them is green as I would like. Why?

As you can see above, I set up four ImageView below my edit text, and they instead get tinted.

   private void setUpTextCursors() {
        Drawable left = getResources().getDrawable(R.drawable.text_select_handle_left_mtrl_alpha);
        Drawable right = getResources().getDrawable(R.drawable.text_select_handle_right_mtrl_alpha);
        Drawable middle = getResources().getDrawable(R.drawable.text_select_handle_middle_mtrl_alpha);
        Drawable cursor = getResources().getDrawable(R.drawable.text_cursor_mtrl_alpha);
        ColorFilter cf = new PorterDuffColorFilter(mGreenColor, PorterDuff.Mode.SRC_IN);

        /**
        * tint my ImageViews, but no effect on edit text handles
        */
        left.setColorFilter(cf); 
        right.setColorFilter(cf);
        middle.setColorFilter(cf);

        /**
        * no effect whatsoever
        */
        cursor.setColorFilter(cf);
   }

Looks like here we have both a 9-patch tinting issue - since filter fails even on test ImageViews - and an issue related to the fact that none of the applied filters get considered by the text selection manager.

Relevant source code about that is from the TextView class and from this Editor hidden helper class which I found somehow. Spent some time on it but still can't tell why my filters are ignored.


To @pskink: let cursor be the filtered drawable, I can have:

<ImageView
    android:id="@id/1"
    android:src="@drawable/cursor_drawable" />

<ImageView 
    android:id="@id/2" />

The first won't be tinted, but if I call imageView2.setBackground(cursor), then it's tinted. Also if I have

<item name="android:textSelectHandle">@drawable/cursor_drawable</item>

this affects the edit selection (because I override the default cursor) but it's not tinted, again.


Solution

  • you need to override the default Resources used by your Activity:

    // your activity source file
    Resources res;
    
    @Override
    public Resources getResources() {
        if (res == null) {
            res = new TintResources(super.getResources());
        }
        return res;
    }
    

    the custom Resources class will override getDrawable() method so you can intercept creating your Drawables and set up the color filter, for example:

    class TintResources extends Resources {
    
        public TintResources(Resources resources) {
            super(resources.getAssets(), resources.getDisplayMetrics(), resources.getConfiguration());
        }
    
        @Override
        public Drawable getDrawable(int id) throws NotFoundException {
            Drawable d = super.getDrawable(id);
            if (id == R.drawable.text_cursor_material) {
                // setup @drawable/text_cursor_material
                d.setColorFilter(0xff00aa00, PorterDuff.Mode.SRC_IN);
            }
            return d;
        }
    }
    

    the same way you can setup other Drawables (@drawable/text_select_handle_*_material), note you need that not direct way since EditText doesn't have getter methods for accessing those Drawables