Mobile Development 13 min read

How to Apply a Global Grayscale Theme to Android Apps Using Custom Views

This article demonstrates how to achieve a global grayscale (black‑and‑white) effect in Android applications by adding simple CSS‑like styles for web pages and creating custom ImageView, TextView, Button, LinearLayout, FrameLayout subclasses that override draw and dispatchDraw methods, and integrating them via Activity onCreateView to replace the default content view.

Sohu Tech Products
Sohu Tech Products
Sohu Tech Products
How to Apply a Global Grayscale Theme to Android Apps Using Custom Views

On April 4 many websites and apps turned black‑and‑white to mourn, and the author shows how to implement the same effect purely with technology.

For a web page, a single CSS rule adds a grayscale filter to the whole page:

html {filter:progid:DXImageTransform.Microsoft.BasicImage(grayscale=1); -webkit-filter: grayscale(100%);}

In Android, the same idea can be applied by creating custom views that modify the canvas drawing.

GrayImageView extends AppCompatImageView and overrides draw(Canvas canvas) to apply a ColorMatrix with zero saturation:

public class GrayImageView extends AppCompatImageView {
    private Paint mPaint = new Paint();
    public GrayImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        ColorMatrix cm = new ColorMatrix();
        cm.setSaturation(0);
        mPaint.setColorFilter(new ColorMatrixColorFilter(cm));
    }
    @Override
    public void draw(Canvas canvas) {
        canvas.saveLayer(null, mPaint, Canvas.ALL_SAVE_FLAG);
        super.draw(canvas);
        canvas.restore();
    }
}

The layout file simply places a normal ImageView and the custom GrayImageView for comparison.

Similarly, GrayTextView and GrayButton are created by extending AppCompatTextView and AppCompatButton with the same paint logic, showing that any widget can be grayed.

To avoid replacing every view individually, a GrayLinearLayout is introduced. By overriding both draw and dispatchDraw , all child views inherit the grayscale effect:

public class GrayLinearLayout extends LinearLayout {
    private Paint mPaint = new Paint();
    public GrayLinearLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        ColorMatrix cm = new ColorMatrix();
        cm.setSaturation(0);
        mPaint.setColorFilter(new ColorMatrixColorFilter(cm));
    }
    @Override
    public void draw(Canvas canvas) {
        canvas.saveLayer(null, mPaint, Canvas.ALL_SAVE_FLAG);
        super.draw(canvas);
        canvas.restore();
    }
    @Override
    protected void dispatchDraw(Canvas canvas) {
        canvas.saveLayer(null, mPaint, Canvas.ALL_SAVE_FLAG);
        super.dispatchDraw(canvas);
        canvas.restore();
    }
}

Replacing the activity’s root layout with GrayLinearLayout instantly grays the whole UI.

For a more universal solution, the author replaces the system android:id/content FrameLayout with a custom GrayFrameLayout by overriding Activity.onCreateView . The method detects when a FrameLayout with the content ID is being inflated and returns a GrayFrameLayout instance instead.

@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
    if ("FrameLayout".equals(name)) {
        int count = attrs.getAttributeCount();
        for (int i = 0; i < count; i++) {
            String attrName = attrs.getAttributeName(i);
            String attrValue = attrs.getAttributeValue(i);
            if (attrName.equals("id")) {
                int id = Integer.parseInt(attrValue.substring(1));
                String idVal = getResources().getResourceName(id);
                if ("android:id/content".equals(idVal)) {
                    GrayFrameLayout gray = new GrayFrameLayout(context, attrs);
                    return gray;
                }
            }
        }
    }
    return super.onCreateView(name, context, attrs);
}

The GrayFrameLayout uses the same paint‑based grayscale logic and also forwards background handling so that window backgrounds are also affected.

Additional notes cover handling of window background drawables, dialog support (which works automatically because dialogs are also part of the view hierarchy), and potential future changes where android:id/content might not be a FrameLayout .

Finally, the author demonstrates the effect on a real project (WanAndroid) where the entire app—including WebView content—appears in grayscale, confirming the approach works across complex UI structures.

UIAndroidcanvasgrayscaleCustomView
Sohu Tech Products
Written by

Sohu Tech Products

A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.