Search code examples
androidporter-duffcolorfilter

PorterDuff color effects in android for views under a given view


Is it possible in android to set up a view in a way that it applies some color filter to everything below that's visible in its bounds? Like in this example:

filtering view

Just a simple rectangular view that inverts colors of everything below it. Of course when user scrolls the list it is also reflected in the inverted box. Is there some easy way to do it using color filters, PorterDuff modes, etc?


Solution

  • You're trying to solve this problem using a view hierarchy like this:

    • Parent
      • ListView
      • InverterView

    Problem is, in this position, InverterView has no control over how ListView is drawn. But you know who does have control over how ListView is drawn? ListView's parent layout does. In other words, what you really want is a hierarchy like this:

    • Parent
      • InverterLayout
        • ListView

    Now InverterLayout is responsible for drawing ListView, and can apply effects to it.

    class InverterLayout extends FrameLayout
    {
        // structure to hold our color filter
        private Paint paint = new Paint();
        // the color filter itself
        private ColorFilter cf;
        // the rectangle we want to invert
        private Rect inversion_rect = new Rect(100, 100, 300, 300);
    
        public InverterLayout(Context context)
        {
            super(context);
            // construct the inversion color matrix
            float[] mat = new float[]
            {
                -1,  0,  0, 0,  255,
                 0, -1,  0, 0,  255,
                 0,  0, -1, 0,  255,
                 0,  0,  0, 1,  0
            };
            cf = new ColorMatrixColorFilter(new ColorMatrix(mat));
        }
    
        @Override protected void dispatchDraw(Canvas c)
        {
            // create a temporary bitmap to draw the child views
            Bitmap b = Bitmap.createBitmap(getWidth(), getHeight(), Config.ARGB_8888);
            Canvas cc = new Canvas(b);
            // draw them to that temporary bitmap
            super.dispatchDraw(cc);
            // copy the temporary bitmap to screen without the inversion filter
            paint.setColorFilter(null);
            c.drawBitmap(b, 0, 0, paint);
            // copy the inverted rectangle
            paint.setColorFilter(cf);
            c.drawBitmap(b, inversion_rect, inversion_rect, paint);
        }
    }
    

    When using this, ensure your child view has its own background. If the view is transparent and the window background shows through, that window background will not be inverted, because the InverterLayout has no control over how the window is drawn.