Skip to content

Automatically mirroring images for RTL layouts

Posted on:October 26, 2023

A simple but awesome trick from a Reddit comment:

Define an integer resource mirrorScale=1 in values folder, and mirrorScale=-1 in values-ldrtl folder. Then you just set android:scaleX="@integer/mirrorScale" in layout xml files.

The following section is my approach. If you want to learn more, please keep reading.

When Localizing Android for Arabic Language (RTL), you will notice that their reading direction is from right to left. Android provides built-in support for RTL layouts and text direction, which automatically mirrors text and layout elements.

However, images do not undergo automatic mirroring. Achieving image mirroring for RTL languages typically involves two approaches, both requiring additional work:

  1. Manually Create Mirrored Images
    Place a mirrored image with the same name in the corresponding ldrtl folder within the drawable directory. For instance, put a mirrored image with the same filename in the drawable-ldrtl-xhdpi folder.
  2. Use android:autoMirrored Attribute
    Do not directly use images, create a drawable resource, for example: @drawable/icon refers to an image that needs to be mirrored
     <?xml version="1.0" encoding="utf-8"?>
     <bitmap xmlns:android="http://schemas.android.com/apk/res/android"
         android:src="@drawable/icon"
         android:autoMirrored="true"/>
    Then use this drawable resource instead.

Achieve by hooking

The 2nd approach mentioned above involves using the android:autoMirrored="true" attribute, which can automatically mirror drawables.

Unfortunately, this attribute cannot be directly used in layout XML files. However, we can utilize Android’s layout inflater mechanism to hook into the view tree generation process and modify ImageView’s src or View’s background.

During the hooking process, we can use drawable.setAutoMirrored(true) to achieve the same effect.

Android provides hook interfaces for this purpose: LayoutInflater.Factory and LayoutInflater.Factory2, with the latter has one additional method. You can directly use Factory2. In Activity onCreate method, inject your custom rtlFactory like this:

val inflater = activity.layoutInflater
inflater.factory2 = rtlFactory

When generates views from XML, it triggers the onCreateView() method within rtlFactory. In this method, we have the ability to replace the generated ImageView objects with our specified custom View objects, such as RtlImageView shown below.

class RtlFactory : LayoutInflater.Factory2 {
    override fun onCreateView(
        parent: View?, name: String, context: Context, attrs: AttributeSet
    ): View? {
        if (name == "ImageView) {
            return RtlImageVIew(context, attrs)
        }
    }
}

Inside the RtlImageView, you can access the image resource defined in the XML attributes, such as android:src="@drawable/ic_logo, and enable automatic mirroring.

// RtlImageView
val drawable = AppCompatResources.getDrawable(view.context, srcResId)
drawable?.isAutoMirrored = true // Enable automatic mirroring
setImageDrawable(drawable)

Of course, you can also add a custom attribute like app:mirrorSrc="true" to RtlImageView to indicate whether automatic mirroring is required. The same principle applies to the View’s background.

Use in XML layout

With the aforementioned setup, in your layout XML files, simply add the app:mirrorSrc="true" attribute to the ImageView whose image needs to be automatically mirrored.

<ImageView
    android:layout_width="26dp"
    android:layout_height="26dp"
    app:mirrorSrc="true"
    android:src="@drawable/icon" />

I have created a library contains a variety of Views for hooking all the Android native Views, including as TextView, Button, FrameLayout, and more.