Android RTL issue in API 24 and higher on locale change
Asked Answered
D

2

3

I was trying to change locale of app at runtime. It is working fine in Andorid below API level 24. But in API level 24 or greater the layout direction is not changing according to locale. Below is the code to change the locale at runtime. I have used LocaleHelper class as below

public class LocaleHelper {

    private static final String SELECTED_LANGUAGE = "Locale.Helper.Selected.Language";

    public static Context onAttach(Context context) {
        String lang = getPersistedData(context, Locale.getDefault().getLanguage());
        return setLocale(context, lang);
    }

    public static Context onAttach(Context context, String defaultLanguage) {
        String lang = getPersistedData(context, defaultLanguage);
        return setLocale(context, lang);
    }

    public static String getLanguage(Context context) {
        return getPersistedData(context, Locale.getDefault().getLanguage());
    }

    public static Context setLocale(Context context, String language) {
        persist(context, language);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            return updateResources(context, language);
        }

        return updateResourcesLegacy(context, language);
    }

    private static String getPersistedData(Context context, String defaultLanguage) {
        SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
        return preferences.getString(SELECTED_LANGUAGE, defaultLanguage);
    }

    private static void persist(Context context, String language) {
        SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
        SharedPreferences.Editor editor = preferences.edit();

        editor.putString(SELECTED_LANGUAGE, language);
        editor.apply();
    }

    @TargetApi(Build.VERSION_CODES.N)
    private static Context updateResources(Context context, String language) {
        Locale locale = new Locale(language);
        Log.d("LocaleHelper", "language : "+language);
        Locale.setDefault(locale);

        Configuration configuration = context.getResources().getConfiguration();
        configuration.setLocale(locale);
        configuration.setLayoutDirection(locale);
        return context.createConfigurationContext(configuration);
    }

    @SuppressWarnings("deprecation")
    private static Context updateResourcesLegacy(Context context, String language) {
        Locale locale = new Locale(language);
        Locale.setDefault(locale);

        Resources resources = context.getResources();

        Configuration configuration = resources.getConfiguration();
        configuration.locale = locale;
        configuration.setLayoutDirection(locale);
        resources.updateConfiguration(configuration, resources.getDisplayMetrics());

        return context;
    }
}

And below is the code I have used in my activity class

public class MainActivity extends AppCompatActivity {
    @BindView(R.id.titleTextView)
    TextView mTitleTextView;
    @BindView(R.id.descTextView)
    TextView mDescTextView;
    @BindView(R.id.aboutTextView)
    TextView mAboutTextView;
    @BindView(R.id.toTRButton)
    Button mToTRButton;
    @BindView(R.id.toENButton)
    Button mToENButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);

        setTitle(getString(R.string.main_activity_toolbar_title));
    }

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(LocaleHelper.onAttach(base));
    }

    @OnClick(R.id.toTRButton)
    public void onChangeToTRClicked() {
        updateViews("ur");
    }

    @OnClick(R.id.toENButton)
    public void onChangeToENClicked() {
        updateViews("en");
    }

    private void updateViews(String languageCode) {
        Context context = LocaleHelper.setLocale(this, languageCode);
        Resources resources = context.getResources();

        mTitleTextView.setText(resources.getString(R.string.main_activity_title));
        mDescTextView.setText(resources.getString(R.string.main_activity_desc));
        mAboutTextView.setText(resources.getString(R.string.main_activity_about));
        mToTRButton.setText(resources.getString(R.string.main_activity_to_tr_button));
        mToENButton.setText(resources.getString(R.string.main_activity_to_en_button));

        setTitle(resources.getString(R.string.main_activity_toolbar_title));
        this.recreate();
    }
}

And in my application class I have added the below code

@Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(LocaleHelper.onAttach(base, "en"));
    }

Now when I change locale from English to Urdu the language gets changed but the layout direction does not change as expected. When I again click to Urdu then layout direction changes(Second attempt). Below are the screenshots for refrence

enter image description here

App language changes but layout direction does not changed

Now layout direction is RTL but it should be LTR for English language

Please help in resolving the issue

Diazotize answered 13/6, 2017 at 7:45 Comment(1)
is your problem resolved with the given answer?Dawna
D
10

The problem seems to be that it doesn't reflect layout direction changes at first update. I solved the problem by overriding the onAttachedToWindow method of Activity like below:

@Override
public void onAttachedToWindow() {
    super.onAttachedToWindow();
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
        getWindow().getDecorView().setLayoutDirection(
                "ur".equals(LocaleHelper.getLanguage(this)) ?
                View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
    }
}

Tested on API 25 and it's working fine. Be careful though I'm not sure about any side effects for this approach at this moment. Nevertheless, I think it is what you are looking for.

I'll also update the blog post to reflect more elegant, generic code for this.

Dawna answered 20/6, 2017 at 22:18 Comment(1)
For me onAttachedToWindow event doesn't fired on activity recreation at runtime. Just placed this block into baseActivity into onCreate, and it solved RTL direction issues, thanks!Afterthought
P
0
package www.ourshopee.com.utils;

import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
import android.preference.PreferenceManager;
import android.support.annotation.RequiresApi;
import android.util.Log;

import java.util.Locale;

public class LocaleHelper {

    private static final String SELECTED_LANGUAGE = "Locale.Helper.Selected.Language";

    public static void onCreate(Context context) {
        String lang = getPersistedData(context, Locale.getDefault().getLanguage());
        setLocale(context, lang);
    }

    public static void onCreate(Context context, String defaultLanguage) {
        String lang = getPersistedData(context, defaultLanguage);
        setLocale(context, lang);
    }

    public static String getLanguage(Context context) {
        return getPersistedData(context, Locale.getDefault().getLanguage());
    }

    public static void setLocale(Context context, String language) {
        persist(context, language);


        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
             updateResources(context, language);
        }

           updateResourcesLegacy(context, language);






    }

    private static String getPersistedData(Context context, String defaultLanguage) {
        SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
        return preferences.getString(SELECTED_LANGUAGE, defaultLanguage);
    }

    private static void persist(Context context, String language) {
        SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
        SharedPreferences.Editor editor = preferences.edit();

        editor.putString(SELECTED_LANGUAGE, language);
        editor.apply();
    }



//    private static void updateResources(Context context, String language) {
//
//
//
//
//
//        Locale locale = new Locale(language);
//        Locale.setDefault(locale);
//        Configuration config = context.getResources().getConfiguration();
//        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
//            config.setLocale(locale);
//        }
//        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
//            context.createConfigurationContext(config);
//        }
//        context.getResources().updateConfiguration(config, context.getResources().getDisplayMetrics());
//
//
//
//
//
//
//    }







    @TargetApi(Build.VERSION_CODES.N)
    private static Context updateResources(Context context, String language) {
        Locale locale = new Locale(language);
        Log.d("LocaleHelper", "language above 24: "+language);
        Locale.setDefault(locale);

        Configuration configuration = context.getResources().getConfiguration();
        configuration.setLocale(locale);
        configuration.setLayoutDirection(locale);
        return context.createConfigurationContext(configuration);
    }

    @SuppressWarnings("deprecation")
    private static Context updateResourcesLegacy(Context context, String language) {
        Locale locale = new Locale(language);
        Log.d("LocaleHelper", "language below 24: "+language);
        Locale.setDefault(locale);

        Resources resources = context.getResources();

        Configuration configuration = resources.getConfiguration();
        configuration.locale = locale;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            configuration.setLayoutDirection(locale);
        }
        resources.updateConfiguration(configuration, resources.getDisplayMetrics());

        return context;
    }

















}
Povertystricken answered 22/5, 2019 at 5:38 Comment(2)
@Dawna i did everything .the layout has been changed.but language still englishPovertystricken
below 24 woking finePovertystricken

© 2022 - 2024 — McMap. All rights reserved.